Rust 代数数据类型中的特征

Rust 代数数据类型中的特征,rust,Rust,我很难理解代数数据类型中关于特征的规则。 下面是一个简化的示例: 使用std::rc::rc; 使用std::cell::RefCell; 庸医{ fn庸医(和自我); } 结构鸭; 鸭子的叫声{ fn嘎嘎(&self){println!(“嘎嘎!”);} } fn main(){ 让mut-pond:Vec=Vec::new(); 让鸭子:盒子=盒子::新(鸭子); pond.push(duck);//这是有效的。 让mut-lake:Vec=Vec::new(); 让mallard:Rc=R

我很难理解代数数据类型中关于特征的规则。 下面是一个简化的示例:

使用std::rc::rc;
使用std::cell::RefCell;
庸医{
fn庸医(和自我);
}
结构鸭;
鸭子的叫声{
fn嘎嘎(&self){println!(“嘎嘎!”);}
}
fn main(){
让mut-pond:Vec=Vec::new();
让鸭子:盒子=盒子::新(鸭子);
pond.push(duck);//这是有效的。
让mut-lake:Vec=Vec::new();
让mallard:Rc=Rc::new(RefCell::new(Box::new(Duck));
lake.push(mallard);//这是类型不匹配。
}
上述代码无法编译,产生以下错误消息:

应为'alloc::rc::rc`,
找到'alloc::rc::rc'`
(庸医,
找到结构“Duck”)[E0308]
src/main.rs:19湖推(绿头鸭);
为什么
pond.push(duck)
有效,而
lake.push(mallard)
无效?在这两种情况下,都提供了一个
Duck
,预计会出现
Quack
。在前者中,编译器很高兴,但在后者中,编译器却不高兴


造成这种差异的原因是否与强制执行有关?

这是一种正确的行为,即使有点不幸

在第一种情况下,我们有:

let mut pond: Vec<Box<Quack>> = Vec::new();
let duck: Box<Duck> = Box::new(Duck);
pond.push(duck);
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);
在第二种情况下,我们有:

let mut pond: Vec<Box<Quack>> = Vec::new();
let duck: Box<Duck> = Box::new(Duck);
pond.push(duck);
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);
现在有麻烦了
Box
是与DST兼容的类型,因此它可以用作trait对象的容器。当
Rc
和其他智能指针实现时,同样的情况很快就会出现。但是,在这种情况下,不存在从具体类型到特征对象的强制,因为
Box
位于其他类型层(
Rc
)的内部

记住,trait对象是一个胖指针,所以
Box
在大小上与
Box
不同。因此,原则上,它们不是直接兼容的:您不能只获取
Box
的字节,然后将它们写入预期的
Box
位置。Rust执行一种特殊的转换,即,它获取指向
Duck
的虚拟表的指针,构造一个胖指针并将其写入
Box
类型的变量

但是,当您拥有
Rc
时,rustc需要知道如何构造和分解
RefCell
Rc
,以便将相同的胖指针转换应用于其内部。当然,因为这些是库类型,所以它不知道如何做。这也适用于任何其他包装类型,例如
Arc
Mutex
甚至
Vec
。您不希望将
Vec
用作
Vec
,对吗

还有一个事实,在带有
Rc
的示例中,从
Box
Box
创建的Rcs不会被连接-它们会有不同的参考计数器

也就是说,只有当您能够直接访问支持DST的智能指针时,才可能发生从具体类型到特征对象的转换,而不是当它隐藏在其他结构中时

这就是说,我看到了在一些选择的类型中允许这样做的可能性。例如,我们可以引入某种编译器已知的
构造
/
展开
特征,它可以用来“到达”包装器堆栈内部,并在其中执行特征对象转换。然而,还没有人设计这个东西并提供RFC——可能是因为它不是一个广泛需要的功能。

解释了 编译器正在运行。基于这些信息,我开发了一个解决方案:创建一个包装器 框周围的结构

包装器称为
QuackWrap
。它有一个固定的大小,它可以像任何一个一样使用 其他结构(我想)。
QuackWrap
中的
允许我构建一个
QuackWrap
围绕实现
庸医
的任何特征。因此,我可以有一个
Vec
其中,内部值是
Duck
s、
Goose
s等的混合物

使用std::rc::rc;
使用std::cell::RefCell;
庸医{
fn庸医(和自我);
}
结构鸭;
鸭子的叫声{
fn嘎嘎(&self){println!(“嘎嘎!”);}
}
结构防震膜(盒);
impl-QuackWrap{

pub fn newNote:
RefCell
在这里是不必要的,我可以用
Rc
重现这个问题。Matthieu,你是对的。
RefCell
不需要使错误发生。根据Vladimir下面的回答,我可以看出为什么使用或不使用
RefCell
时会发生相同的错误。谢谢!你如何实现这个模式模式我的意思是:你有一堆可变对象。为了提高效率,可以通过不同的查找结构访问它们,例如哈希、二进制托盘和向量。其中一些查找结构需要不同类型的对象,所有这些对象都实现了一个特点。我可以将它们包装在枚举中。或者我可以创建拥有方框的包装结构。一致对于此RFC(),目标包装器类型应实现强制已nsized,以使特性自动协同成为可能。但是,RefCell目前未实现此实现,但仍然可以对基于堆栈的实体进行协同().Box实现了强制eunsized,并且可以单独共享,但在RefCell中不能,这非常奇怪。我尝试实现了自己的RefCell,该RefCell实现了强制eunsized,但没有成功(),更不清楚为什么会这样,因为RefCell只是UnsafeCell的运行时借用检查器,它只是一个包装器,就像下面示例中的QuackWrap一样。啊,我明白了。这里的框充当&T,所以除了在保存trait对象时内部值的vtable cut之外,它也不会向wra对象提供完整的vtable信息p it,这就是为什么到目前为止还不可能强制转换包装的DST类型。看起来我们需要使用不安全的,并使用手动vtable管理创建一个新的DST类型包装器。本文也对此进行了很好的描述