Rust泛型/特征:"预期'Foo <B>',发现'Foo <Foo2>'"

goo*_*goo 4 rust

我问过类似的早期问题已让我明白是怎么回事引擎盖下,但我仍然不能得到锈做我想做的事情,当谈到泛型编程.这是一些代码:

struct Foo<B: Bar> { bars: Vec<Box<B>> }

struct Foo2;

trait Bar {}

impl Bar for Foo2 {}

impl<B: Bar> Foo<B> {
  fn do_something() -> Foo<B> {
    let foo2:Box<Bar> = box Foo2;
    let mut foo = Foo { bars: vec!(box Foo2) };
    foo.bars.push(box Foo2);
    foo // compiler: *ERROR*
  }
}
Run Code Online (Sandbox Code Playgroud)

错误: expected 'Foo<B>', found 'Foo<Foo2>'

  1. 我怎样才能给编译器一个提示或明确告诉编译器foo(Foo)implements Bar(B: Bar)?
  2. 这是一个错误吗?我应该推迟使用Rust直到它达到1.0?

版: 0.12.0-nightly (4d69696ff 2014-09-24 20:35:52 +0000)


我在@Levans的解决方案中看到的问题:

struct Foo2;

struct Foo3 {
  a: int
}

trait Bar {
    fn create_bar() -> Self;
}

impl Bar for Foo2 {
    fn create_bar() -> Foo2 { Foo2 } // will work
}

impl Bar for Foo3 {
    fn create_bar(a: int) -> Foo3 { Foo3 {a: a} } // will not work
}
Run Code Online (Sandbox Code Playgroud)

错误: method 'create_bar' has 1 parameter but the declaration in trait 'Bar::create_bar' has 0

另外,我注意到了这一点:Bar::create_bar().Rust会如何知道使用它Foo2的实现?

Lev*_*ans 7

当您使用<B: Bar>告诉编译器来定义函数时,"您可以B通过任何实现特征的类型替换此函数Bar".

例如,如果您创建一个结构Foo3实现特质Bar以及,编译器会期望能够调用do_somethingB幸福Foo3,这是不可能与你当前的实现.

在你的情况下,你的do_something函数试图创建一个B对象,因此它需要一个通用的方法,由Bar特征给出,作为一个create_bar()方法,例如,像这样:

struct Foo<B: Bar> { bars: Vec<Box<B>> }

struct Foo2;

trait Bar {
    fn create_bar() -> Self;
}

impl Bar for Foo2 {
    fn create_bar() -> Foo2 { Foo2 }
}

impl<B: Bar> Foo<B> {
  fn do_something() -> Foo<B> {
    let mut foo = Foo { bars: vec!(box Bar::create_bar()) }; 
    foo.bars.push(box Bar::create_bar());
    foo 
  }
}
Run Code Online (Sandbox Code Playgroud)

回答编辑:

在你的代码中,它确实不会起作用,因为你希望传递更多的参数create_bar,这是不可能的,因为它不尊重create_bar不带任何参数的特征定义.

但是像这样的东西可以毫无问题地工作:

struct Foo2;

struct Foo3 {
  a: int
}

trait Bar {
    fn create_bar() -> Self;
}

impl Bar for Foo2 {
    fn create_bar() -> Foo2 { Foo2 }
}

impl Bar for Foo3 {
    fn create_bar() -> Foo3 { Foo3 {a: Ou} }
}
Run Code Online (Sandbox Code Playgroud)

关键是:如果没有通用的方法,你的do_something函数就无法创建Bar对象<B>,只要它实现,这种方式不依赖于哪种类型Bar.这就是泛型是如何工作的:如果你打电话do_something::<Foo2>(),这是完全一样,如果你换成BFoo2你的函数的定义整.

然而,我怀疑你真正要做的是存储不同类型,所有都Bar在同一个Vec中实现,(否则将一个Box包装在内部将是非常无用的),你可以使用trait对象实现这一点,并且它不需要泛型:

struct Foo<'a> { bars: Vec<Box<Bar + 'a>> }

struct Foo2;

trait Bar {}

impl Bar for Foo2 {}

impl<'a> Foo<'a> {
  fn do_something() -> Foo<'a> {
    let mut foo = Foo { bars: vec!(box Foo2 as Box<Bar>) };
    foo.bars.push(box Foo2 as Box<Bar>);
    foo
  }
}
Run Code Online (Sandbox Code Playgroud)

基本上,Trait对象是对象的引用或指针,被转换为Trait:

let foo2 = Foo2;
let bar = &foo2 as &Bar; // bar is a reference to a Trait object Bar
Run Code Online (Sandbox Code Playgroud)

正如我的例子所示,它也适用于Boxes.