除非使用临时变量,否则为什么不能插入dyn Trait Vec?

时间:2020-05-23 12:53:42

标签: rust traits

这是我的代码:

use std::rc::{Rc, Weak};
use std::cell::RefCell;

trait Trait {}

fn push<E: Trait>(e: E) {
    let mut v: Vec<Rc<RefCell<Box<dyn Trait>>>> = Vec::new();

    // let x = Rc::new(RefCell::new(Box::new(e)));
    // v.push(x); // error

    v.push(Rc::new(RefCell::new(Box::new(e)))); // works fine
}

v.push(x)引发此错误:

error[E0308]: mismatched types
  --> src/main.rs:12:12
   |
7  | fn push<E: Trait>(e: E) {
   |         - this type parameter
...
12 |     v.push(x);
   |            ^ expected trait object `dyn Trait`, found type parameter `E`
   |
   = note: expected struct `std::rc::Rc<std::cell::RefCell<std::boxed::Box<dyn Trait>>>`
              found struct `std::rc::Rc<std::cell::RefCell<std::boxed::Box<E>>>`
   = help: type parameters must be constrained to match other types
   = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters

但是,如果我将值(具有完全相同的值和类型的值)直接推入向量,则可以正确编译。

那么为什么第一个版本不编译?我应该做些什么改变,以便在将其推入向量之前可以使用x

1 个答案:

答案 0 :(得分:6)

全部在类型推断中。当您写时:

v.push(Rc::new(RefCell::new(Box::new(e))));

从此上下文中,Rust可以知道RefCell::new()的参数必须为Box<dyn Trait>,因此尽管提供了Box<E>,它仍将其强制为前一种。另一方面,当您编写此代码时:

let x = Rc::new(RefCell::new(Box::new(e)));
v.push(x); // compile error

首先请推断出x类型的Rc<RefCell<Box<E>>>,您将无法再push转换成vec的{​​{1}}。您可以通过在Rc<RefCell<Box<dyn Trait>>>绑定中放置显式类型注释来告知Rust预先确实需要let来更改此设置:

Rc<RefCell<Box<dyn Trait>>>

playground

这里要理解的重要一点是use std::rc::{Rc, Weak}; use std::cell::RefCell; trait Trait {} fn push<E: Trait>(e: E) { let mut v: Vec<Rc<RefCell<Box<dyn Trait>>>> = Vec::new(); let x: Rc<RefCell<Box<dyn Trait>>> = Rc::new(RefCell::new(Box::new(e))); v.push(x); // compiles } E不同dyn TraitE的一些已知的具体实现,而Trait是一个特征对象,其底层的具体实现被删除了。