Rust仿制品/特性:“预期'Foo <b>',发现'Foo <foo2>'”</foo2> </b>

时间:2014-09-26 15:56:40

标签: rust

我问了一个类似的question earlier,它帮助我理解了幕后发生的事情但是我仍然无法让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*
  }
}

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

  1. 如何为编译器提供提示或明确告诉编译器fooFoo)实现BarB: Bar)?
  2. 这是一个错误吗?我应该继续使用Rust直到它达到1.0?
  3. 版本: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
    }
    

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

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

1 个答案:

答案 0 :(得分:6)

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

例如,如果您创建了一个实现特征Foo3的结构Bar,编译器也可以调用do_something BFoo3 ,这对于您当前的实现是不可能的。

在您的情况下,您的do_something函数会尝试创建一个B对象,因此需要一种通用的方法来执行此操作,由Bar特征给出,作为{{1}例如,方法如下:

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 } } ,这是不可能的,因为它不尊重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} } } 函数无法创建do_something对象而没有通用的方法,这种方式不依赖于Bar中的哪种类型,前提是它实现了<B>。这就是泛型的工作原理:如果你打电话给Bar,就好像你在函数的整个定义中将do_something::<Foo2>()替换为B一样。

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

Bar

基本上,Trait对象是对象的引用或指针,被转换为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
  }
}

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