【问题标题】:"expected type parameter, found struct"“预期类型参数,找到结构”
【发布时间】:2014-11-20 21:31:37
【问题描述】:

我一直在使用 Rust 处理特征,但遇到了一个问题。这是一些代码:

struct Foo;

trait Bar {}

impl Bar for Foo {}

fn bar<B: Bar>(bar: B) {}

fn barr<B: Bar>() {
    bar(Foo); // 1. THIS WILL WORK
    let foo: B = Foo; // 2. THIS WILL NOT WORK
    let foo_vec: Vec<B> = vec![Foo]; // 3. THIS WILL NOT WORK
}

这会产生错误:

error[E0308]: mismatched types
  --> src/main.rs:11:18
   |
11 |     let foo: B = Foo; // 2. THIS WILL NOT WORK
   |                  ^^^ expected type parameter, found struct `Foo`
   |
   = note: expected type `B`
              found type `Foo`

error[E0308]: mismatched types
  --> src/main.rs:12:32
   |
12 |     let foo_vec: Vec<B> = vec![Foo]; // 3. THIS WILL NOT WORK
   |                                ^^^ expected type parameter, found struct `Foo`
   |
   = note: expected type `_`
              found type `Foo`

为什么 #2 和 #3 不起作用?我怎样才能让编译器知道 Foo 实际上有一个 Bar impl


另一个例子:

struct Foo<B: Bar> {
    bar: Option<B>,
}

struct Foo2;

trait Bar {}

impl<B: Bar> Bar for Foo<B> {}

impl Bar for Foo2 {}

fn bar<B: Bar>(bar: B) {}

fn circle_vec<B: Bar>() {
    bar(Foo2); // 1. WORKS
    Foo { bar: Some(Foo { bar: None }) }; // 2. WILL NOT WORK
}

这会给我这个错误:

error[E0282]: type annotations needed
  --> src/main.rs:17:21
   |
17 |     Foo { bar: Some(Foo { bar: None }) }; // 2. WILL NOT WORK
   |                     ^^^ cannot infer type for `B`

【问题讨论】:

    标签: rust


    【解决方案1】:

    你有两个不同的问题,所以我想我会写两个不同的答案。


    在您的第一个代码示例中,2 和 3 不起作用,因为 B 是 input 类型参数;是 barr 的调用者决定 B 是什么。但是,您试图将其强制为 Foo

    假设我们有另一个Bar 的实现:

    struct Quux;
    
    impl Bar for Quux {}
    

    假设我们这样调用barr

    barr::<Quux>()
    

    barr 基本上会这样编译:

    fn barr() {
        bar(Foo);
        let foo: Quux = Foo;
        let foo_vec: Vec<Quux> = vec![Foo];
    }
    

    FooQuux 不兼容,Vec&lt;Foo&gt;Vec&lt;Quux&gt; 也不兼容。

    如果您尝试创建任意Bar 对象的向量,则需要以非通用方式使用Bar。由于 trait 类型没有大小,您不能将它们直接存储在 Vec 中,因此您必须使用 Vec&lt;Box&lt;Bar&gt;&gt;Vec&lt;&amp;Bar&gt; 或其他包含指针的类型。

    fn barr() {
        bar(Foo);
        let foo: Box<Bar> = Box::new(Foo);
        let foo_vec: Vec<Box<Bar>> = vec![Box::new(Foo) as Box<Bar>];
    }
    

    在您的第二个代码示例中,错误是None 的类型为Option&lt;T&gt;,编译器无法推断T 的适当类型。我们可以像这样显式指定T

    fn circle_vec<B: Bar>() {
        bar(Foo2);
        Foo {
            bar: Some(Foo { bar: None::<Foo2> }),
        };
    }
    

    【讨论】:

    • 最后一个问题,为什么fn barr&lt;B: Bar&gt;() { let foo_vec: Vec&lt;Box&lt;B&gt;&gt; ...} 不起作用?或者它可以工作,我只是错过了一些东西?
    • 通过使用类型参数,你的向量的所有元素必须是由调用者指定的相同类型,这意味着你不能在向量中存储 Foo 因为B 可能不会是Foo。要将元素添加到向量中,您需要 B 类型的值,您通常会将其作为函数的参数接收 (fn barr&lt;B: Bar&gt;(x: B))。
    • 这似乎是在使用 trait 对象,即动态调度。然而,the Rust Programming Language book on this topic 说“总是可以有一个瘦的静态分派包装器函数来执行动态分派,但反之亦然,这意味着静态调用更加灵活。”实际上有没有办法在这种情况下不使用特征对象而只执行静态调度?我还遇到了一个用例,我希望有一个向量存储实现相同特征的可能不同类型的结构。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-13
    • 2021-03-11
    • 2018-02-07
    • 2017-09-13
    • 1970-01-01
    相关资源
    最近更新 更多