【问题标题】:Incorrect type inference for Rust vector of trait object特征对象的 Rust 向量的类型推断不正确
【发布时间】:2021-04-01 09:25:01
【问题描述】:

我无法理解以下错误:

// test.rs

struct A {
    a: u32
}

trait B { }

impl B for A { }

struct C {
    c: Vec<Box<dyn B>>,
}

fn test() {
    let a = A {a: 1};
    let a_vec = vec![Box::new(a)];
    let c = C {
        c: a_vec,
        // c: vec![Box::new(a)],
    };
}

编译错误:

mismatched types

expected trait object `dyn test::B`, found struct `test::A`

错误发生在我尝试创建 C 的那一行。有趣的是,如果我按以下方式创建 C,那么它可以正常编译。

let c = C {
        c: vec![Box::new(a)],
    };

另一种可行的方法是

let a_vec: Vec<Box<dyn B>> = vec![Box::new(a)];
let c = C {
        c: a_vec,
    };

我做的另一个实验是将 C.c 的类型更改为 Box 而不是 Vec,然后无论我如何启动它都会编译。

在我看来,这可能是 Rust 关于特征对象向量的类型推断的一些缺失功能/错误?我对 Rust 还是很陌生,所以对此的任何想法都非常感谢!

【问题讨论】:

  • 另一个有效的变体是vec![Box::new(a) as _]。错误本身是由于瘦指针(盒装具体结构)和胖指针(盒装 dyn 特征)之间的差异造成的。一个可以转换为另一个(假设结构实现了特征),但有时编译器需要一些轻推来执行转换。这是类型推断的缺陷还是某些更深层次原理的结果,这是一个很好的问题。

标签: rust trait-objects


【解决方案1】:

这是所有预期的行为。 Box&lt;A&gt; 可以强制转换为Box&lt;dyn B&gt;——这称为unsized coercion,只要编译器知道目标类型是Box&lt;dyn B&gt;,它就会隐式发生。但是,刚写的时候

let a_vec = vec![Box::new(a)];

编译器不知道向量的项类型,因此它从右侧的表达式推断它,这意味着a_vecVec&lt;Box&lt;A&gt;&gt; 的类型结束。

Vec&lt;Box&lt;A&gt;&gt;Vec&lt;Box&lt;dyn B&gt;&gt; 没有大小不一的强制转换。将Box&lt;A&gt; 转换为Box&lt;dyn B&gt; 仅意味着将指针转换为堆栈上的胖指针,这是一种廉价的操作。转换这些元素的向量是非常不同的——它需要重新分配整个向量并取消每个元素的大小,这是一个相当昂贵的操作,所以它不应该隐式发生。

在所有实际编译的版本中,向量从一开始就被创建为Vec&lt;Box&lt;dyn B&gt;&gt;,因为你告诉编译器这是你想要的类型。

【讨论】:

  • 这是所有预期的行为 - 我不希望的部分是 vec![Box::new(a)] 不起作用,但 vec![Box::new(a) as _] 起作用。 as _ 似乎是多余的——它为编译器提供了哪些没有它就无法推断的新信息?
  • @user4815162342 实际上在某种意义上给编译器less信息。编译器尝试根据右侧推断a_vec 的类型。如果右边是vec![Box::new(a)],编译器可以推断出一个完整的类型,Vec&lt;Box&lt;A&gt;&gt;,因为那是表达式的类型。如果加上as _,编译器就不能再推断出完整的类型,只能推断Vec&lt;_&gt;c 的初始化然后给编译器进一步的提示 _ 实际上是 Box&lt;dyn B&gt;
  • 谢谢。我试图理解为什么编译器会推断出let a_vec = vec![Box::new(a); /* use a_vec where Vec&lt;Box&lt;dyn T&gt;&gt; is expected */ 的向量的完整类型,而不是let c = C { c: vec![Box::new(a)] }。我希望如果前者由于类型不匹配而无法编译,那么后者也是如此。或者,换句话说,如果为 OP 的代码编译而更改了编译器,在某些情况下会导致问题吗?
  • @user4815162342 在let c = C { c: vec![Box::new(a)] } 中,没有可推断的类型。结构体Cc 字段的类型是已知的。
猜你喜欢
  • 2021-04-15
  • 1970-01-01
  • 1970-01-01
  • 2021-06-07
  • 2013-09-17
  • 1970-01-01
  • 1970-01-01
  • 2022-10-05
  • 1970-01-01
相关资源
最近更新 更多