在Rust中如何在泛型特质上实现特定类型?

25

最初我认为你可以做到这一点,因为文档 (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);
                  ^~~~~~~~~~~~~~~

我不知道那是什么意思。

你如何做到这一点?

1个回答

17
impl Bar<int> for Fooimpl Bar<uint> for Foo 是错误的,因为目前每个 trait、类型对只允许一个 impl(忽略 trait 上的参数)。我在这篇答案中详细解释了这个问题,包括使用第二个 trait 的解决方法,避免使 Foo 成为泛型(这可能不是你想要的)。请注意保留 HTML 标签。
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::<int>::...来指定,但我认为这不是你想要的,相反,你应该在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::<()>::new::<int>(0.0) 将会得到 y == 0.0,但是 Foo::<u64>::new::<int>(0.0) 将会得到 y == 8.0


很好的回答~我想知道你是否可以澄清第二部分的一个小问题。impl<T> Foo<T> { fn new<T>() -> Array<T> } 也不起作用。它仍然会出现有关未约束类型的错误。但是,如果我从 impl<T> Foo<T> {} 中删除 new 并将其放入 impl Foo<()> {} 中,则可以正常工作。为什么?为什么 -> Array<U> 在返回类型上不能推断出 Array::<U>?...而这个 <()> 是什么让它能够正常工作? - Doug
1
@Doug 第一个情况是定义一个新的类型参数,也称为T,就像let a = 1; { let a = 2; }定义了内部作用域中的新变量a一样。Foo<()>的情况可以工作,因为您只有从new<T>() -> Array<T>中的类型参数,因为您为Foo的类型参数指定了一个具体类型((), 即 unit): 它不再是通用的。-> Array<U>不会推断返回类型,因为那是错误的,没有理由UT必须相同... - huon
你可能会有以下的编程问题:impl<T> Array<T> { fn convert<U>(&self) -> Array<U> { ... } },这个实现将创建一个不同类型的数组。但是只是因为你在代码中提到了 Array<...> 并不意味着它必须与 impl 中的 Array<...> 相匹配。 - huon
(为了明确起见,<()> 这个东西只是将 () 类型作为类型参数传递,impl Foo<uint> { ... } 也同样“可行”,但几乎肯定不是你真正想要的。) - huon
4
这个限制已经被RFC 0048取消了,现在允许多个(trait, type)对,只要类型参数不同即可。 - Tomáš Gavenčiak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接