这是正确的行为,尽管有些不幸。
在第一种情况下,我们有这样的:
let mut pond: Vec<Box<Quack>> = Vec::new();
let duck: Box<Duck> = Box::new(Duck);
pond.push(duck);
注意push()
, 当被调用时Vec<Box<Quack>>
,接受Box<Quack>
,而你正在通过Box<Duck>
。这是可以的 - rustc 能够理解您想要将装箱值转换为特征对象,如下所示:
let duck: Box<Duck> = Box::new(Duck);
let quack: Box<Quack> = duck; // automatic coercion to a trait object
在第二种情况下,我们有这样的:
let mut lake: Vec<Rc<RefCell<Box<Quack>>>> = Vec::new();
let mallard: Rc<RefCell<Box<Duck>>> = Rc::new(RefCell::new(Box::new(Duck)));
lake.push(mallard);
Here push()
接受Rc<RefCell<Box<Quack>>>
当你提供Rc<RefCell<Box<Duck>>>
:
let mallard: Rc<RefCell<Box<Duck>>> = Rc::new(RefCell::new(Box::new(Duck)));
let quack: Rc<RefCell<Box<Quack>>> = mallard;
现在有麻烦了。Box<T>
是 DST 兼容类型,因此它可以用作特征对象的容器。同样的事情很快也会发生在Rc
和其他智能指针时this RFC https://github.com/rust-lang/rfcs/blob/master/text/0982-dst-coercion.md已实施。然而,在这种情况下,没有从具体类型到特征对象的强制,因为Box<Duck>
位于类型的附加层内部(Rc<RefCell<..>>
).
请记住,trait 对象是一个胖指针,所以Box<Duck>
不同于Box<Quack>
在尺寸方面。因此,原则上,它们不直接兼容:你不能只获取字节Box<Duck>
并将它们写到哪里Box<Quack>
是期待。 Rust执行一个特殊的转换,即它获得一个指向虚表的指针Duck
,构造一个胖指针并将其写入Box<Quack>
- 类型变量。
当你有Rc<RefCell<Box<Duck>>>
然而,rustc 需要知道如何构造和解构两者RefCell
and Rc
为了对其内部应用相同的胖指针转换。当然,因为这些是库类型,所以它不知道该怎么做。对于任何其他包装器类型也是如此,例如Arc
or Mutex
甚至Vec
。您没想到可以使用Vec<Box<Duck>>
as Vec<Box<Quack>>
, right?
还有一个事实是,在示例中Rc
创建的 RcsBox<Duck>
and Box<Quack>
不会被连接 - 他们会有不同的参考计数器。
也就是说,只有当您可以直接访问支持 DST 的智能指针时,才会发生从具体类型到特征对象的转换,而不是当它隐藏在其他结构中时。
也就是说,我明白了may be可以允许某些选择类型这样做。例如,我们可以引入某种Construct
/Unwrap
编译器已知的特征,编译器可以使用这些特征“到达”包装器堆栈内部并在其中执行特征对象转换。然而,还没有人设计这个东西并提供有关它的 RFC - 可能是因为它不是一个广泛需要的功能。