【问题标题】:How do you implement specific types on generic traits in rust?你如何在 rust 中实现泛型特征的特定类型?
【发布时间】:2014-07-16 02:29:41
【问题描述】:

我最初认为您可以这样做,因为文档 (http://doc.rust-lang.org/rust.html#implementations) 建议您可以:

trait Bar<T> {
  fn ex(&self) -> T;
}

struct Foo {
  y:f64
}

impl Bar<int> for Foo {
  fn ex(&self) -> int {
    return self.y.floor() as int;
  }
}

impl Bar<uint> for Foo {
  fn ex(&self) -> uint {
    if (self.y < 0.0) {
      return 0u;
    }
    return self.y.floor() as uint;
  }
}

...但这似乎不起作用。我收到如下错误:

error: multiple applicable methods in scope
error: expected Bar<uint>, but found Bar<int> (expected uint but found int)
error: expected Bar<int>, but found Bar<uint> (expected int but found uint)

所以我想也许 Foo 必须是通用的才能工作,所以每个特定的 Foo 都有它自己的 Bar 实现:

trait Bar<T> {
  fn ex(&self) -> T;
}

struct Foo<T> {
  y:f64
}

impl<T> Foo<T> {
  fn new<U>(value:f64) -> Foo<U> {
    return Foo { y: value } as Foo<U>;
  }
}

impl Bar<int> for Foo<int> {
  fn ex(&self) -> int {
    return self.y.floor() as int;
  }
}

impl Bar<uint> for Foo<uint> {
  fn ex(&self) -> uint {
    if (self.y < 0.0) {
      return 0u;
    }
    return self.y.floor() as uint;
  }
}

fn main() {
  let z = Foo::new::<int>(100.5);
  let q = Foo::new::<uint>(101.5);
  let i:int = z.ex();
  let j:uint = q.ex();
}

...但我的构造函数似乎不起作用:

x.rs:11:12: 11:38 error: non-scalar cast: `Foo<<generic #1>>` as `Foo<U>`
x.rs:11     return Foo { y: value } as Foo<U>;
                   ^~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to previous error

编辑:我也试过了:

impl<T> Foo<T> {
  fn new<U>(value:f64) -> Foo<U> {
    let rtn:Foo<U> = Foo { y: value };
    return rtn;
  }
}

这解决了转换错误,但导致:

x.rs:32:11: 32:26 error: cannot determine a type for this expression: unconstrained type
x.rs:32   let z = Foo::new::<int>(100.5);
                  ^~~~~~~~~~~~~~~

O_o 我不知道那是什么意思。

你是怎么做到的?

【问题讨论】:

    标签: rust


    【解决方案1】:

    impl Bar&lt;int&gt; for Fooimpl Bar&lt;uint&gt; for Foo 是一个错误,因为目前每个特征只允许一个 impl,类型对(忽略特征上的参数)。 I went into more detail in this answer,包括解决使用次要特征的工作,以避免必须使 Foo 泛型(这可能不是您想要的)。

    trait BarForFoo {
        fn do_ex(foo: &Foo) -> Self;
    }
    impl BarForFoo for int {
        fn do_ex(foo: &Foo) -> int {
            foo.y.floor() as int
        }
    }    
    impl BarForFoo for uint {
        fn do_ex(foo: &Foo) -> uint {
            foo.y.max(0.0).floor() as uint
        }
    }
    
    impl<T: BarForFoo> Bar<T> for Foo {
        fn ex(&self) -> T { BarForFoo::do_ex(self) }
    }
    

    第二个错误是因为 new 函数有两个类型参数 TU“在范围内”,但只指定了一个 (U)。 T 需要通过编写 Foo::&lt;int&gt;::... 来指定,但是我认为这不是您想要的,相反,您应该在 new 函数中使用 T 泛型:

    impl<T> Foo<T> {
        fn new(value: f64) -> Foo<T> { ... }
    }
    

    作为背景,编译器需要知道T 的具体类型,因为new 的实现可能会改变:

    impl<T> Foo<T> {
      fn new<U>(value:f64) -> Foo<U> {
        Foo { y: value + std::mem::size_of::<T>() as f64 }
      }
    }
    

    然后Foo::&lt;()&gt;::new::&lt;int&gt;(0.0) 会给出y == 0.0,但Foo::&lt;u64&gt;::new::&lt;int&gt;(0.0) 会给出y == 8.0

    【讨论】:

    • 很好的答案~我想知道你是否可以从第二部分澄清一点。 impl Foo { fn new() -> Array } 也不起作用。它仍然是关于无约束类型的错误。但是,如果我从 impl Foo {} 中删除 new 并将其放入 impl Foo { } 它工作正常。这是为什么?为什么 -> Array 不能在返回类型上推断 Array::? ...而这个 是什么使它起作用?
    • @Doug 第一种情况是定义一个新的类型参数,也称为T,与let a = 1; { let a = 2; } 在内部范围内定义一个新变量a 相同。 Foo&lt;()&gt; 案例之所以有效,是因为您只有来自 new&lt;T&gt;() -&gt; Array&lt;T&gt; 的类型参数,因为您已经为 Foo 的类型参数指定了具体类型 ((), aka unit):它不再是通用的。 -&gt; Array&lt;U&gt; 不会推断返回类型,因为那是错误的,没有理由 UT 必须相同......
    • 例如你可能有 impl&lt;T&gt; Array&lt;T&gt; { fn convert&lt;U&gt;(&amp;self) -&gt; Array&lt;U&gt; { ... } } 正在制作一个不同类型的数组:仅仅因为你提到 Array&lt;...&gt; 并不意味着它必须匹配 impl 标头中的 Array&lt;...&gt;
    • (需要明确的是,&lt;()&gt; 只是将 () 类型作为类型参数传递,impl Foo&lt;uint&gt; { ... } 也同样可以“工作”,但几乎可以肯定不是您实际使用的想要。)
    • @Doug 这个限制已被RFC 0048 移除,当类型参数不同时,现在允许多个(trait, type) 对。
    猜你喜欢
    • 1970-01-01
    • 2021-07-02
    • 2023-01-12
    • 2020-12-24
    • 1970-01-01
    • 2022-08-15
    • 2022-11-23
    • 2020-05-05
    • 1970-01-01
    相关资源
    最近更新 更多