首页 文章

Rust generics / traits:“期待'Foo<B>',找到'Foo<Foo2>'”

提问于
浏览
3

我问了一个类似的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>'

  • 如何给编译器提示或明确告诉编译器 fooFoo )实现 BarB: Bar )?

  • 这是一个错误吗?我应该推迟使用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
}

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

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

1 回答

  • 6

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

    例如,如果您创建了一个实现trait Bar 的struct Foo3 ,那么编译器可以调用 do_somethingBFoo3 ,这对于您当前的实现是不可能的 .

    在您的情况下,您的 do_something 函数会尝试创建一个 B 对象,因此它需要一个通用的方法来执行此操作,由 Bar trait给出,例如 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 
      }
    }
    

    Answer to edit :

    在你的代码中,它确实不会起作用,因为你希望将更多的参数传递给 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>() ,就好像你在函数的整个定义中用 Foo2 替换了 B .

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

    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
      }
    }
    

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

    let foo2 = Foo2;
    let bar = &foo2 as &Bar; // bar is a reference to a Trait object Bar
    

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

相关问题