【问题标题】:Rust generics/traits: "expected 'Foo<B>', found 'Foo<Foo2>'"Rust 泛型/特征:“预期 'Foo<B>',发现 'Foo<Foo2>'”
【发布时间】:2014-11-21 16:52:12
【问题描述】:

我问了一个类似的 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&lt;B&gt;', found 'Foo&lt;Foo2&gt;'

  1. 如何给编译器提示或明确告诉编译器foo (Foo) 实现了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
}

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

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

【问题讨论】:

    标签: rust


    【解决方案1】:

    当您使用 &lt;B: Bar&gt; 定义函数时,您是在告诉编译器“您可以在此函数中替换 B 为实现特征 Bar 的任何类型”。

    例如,如果您创建了一个结构 Foo3 也实现了 trait Bar,编译器期望能够调用 do_something,而 BFoo3,这在您当前的情况下是不可能的实施。

    在您的情况下,您的 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 
      }
    }
    

    回答编辑:

    在您的代码中,它确实不起作用,因为您希望将更多参数传递给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 对象,这种方法不依赖于&lt;B&gt; 中的类型,只要它实现了Bar。这就是泛型的工作原理:如果您调用 do_something::&lt;Foo2&gt;(),就好像在整个函数定义中将 B 替换为 Foo2

    然而,我怀疑你真正想要做的是存储 differents 类型,所有在同一个 Vec 中实现 Bar,(否则在里面包装一个 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
      }
    }
    

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

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

    正如我的示例中所提供的,它也适用于 Box。

    【讨论】:

    • 我预见到这个解决方案会出现问题。如果我有许多不同的结构实现Bar 但有不同的字段怎么办?请参阅上面的编辑以查看示例
    • 我想这就是问题所在。谢谢。此外,您的代码不会编译。错误:explicit lifetime bound required
    猜你喜欢
    • 2023-04-01
    • 1970-01-01
    • 2020-05-09
    • 2015-09-16
    • 1970-01-01
    • 2020-12-24
    • 1970-01-01
    • 1970-01-01
    • 2012-08-27
    相关资源
    最近更新 更多