期望类型参数,但找到了结构体。

11

我一直在使用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确实有一个Barimpl


另一个例子:

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`
1个回答

14

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


在您的第一个代码示例中,2和3不起作用,因为B是一个输入类型参数;它是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<Foo>Vec<Quux>也不兼容。

如果你想创建任意Bar对象的向量,你需要以非泛型方式使用Bar。由于特质类型是不定大小的,你不能直接在Vec中存储它们,因此必须使用Vec<Box<Bar>>Vec<&Bar>或其他包装指针的类型。

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<T>,编译器无法推断出适当的T类型。我们可以像这样明确指定T
fn circle_vec<B: Bar>() {
    bar(Foo2);
    Foo {
        bar: Some(Foo { bar: None::<Foo2> }),
    };
}

最后一个问题,为什么 fn barr<B: Bar>() { let foo_vec: Vec<Box<B>> ...} 不能工作?或者它可以工作,只是我漏掉了什么? - goo
2
通过使用类型参数,您的向量中的所有元素必须是由调用方指定的相同类型,这意味着您不能将 'Foo' 存储在向量中,因为 B 可能不是 Foo。要向向量中添加元素,您需要具有类型 B 的值,通常作为函数参数接收(fn barr<B: Bar>(x: B))。 - Francis Gagné
1
这似乎使用特质对象,即动态分发。然而,Rust编程语言书籍关于此主题的章节说“可以始终有一个薄的静态分派包装函数来执行动态分派,但反之不行,这意味着静态调用更加灵活。”在这种情况下,是否实际上有一种不使用特质对象,只执行静态分派的方法?我还遇到了这样的用例,我想要有一个向量存储可能不同类型的结构体,这些结构体实现了相同的特质。 - xji

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接