几天前,有一个有关OOP的问题,希望使用垂头丧气的方式解决该问题。作为一个自我挑战,我尝试使用 std::mem::transmute
和不安全的块来解决此问题,这会产生段错误。
这是full code in Rust Playground。
代码的令人讨厌的部分是这样的:
unsafe {
let storage =
mem::transmute::<&mut Box<AnyStorable + 'static>, &mut Box<Insertable<C>>>(x);
println!("{:#?}", x);
storage.insert(component); // This segfaults
};
运行时会产生段错误:
但是,当我替换此行时:
let storage =
mem::transmute::<&mut Box<AnyStorable + 'static>, &mut Box<Insertable<C>>>(x);
和:
let storage =
mem::transmute::<&mut Box<AnyStorable + 'static>, &mut Box<VecStorage<C>>>(x);
有用。为什么第一行失败而第二行没有失败?
最佳答案
Box<SomeTrait>
存储两个指针:一个指向对象,另一个指向vtable。 Box<SomeType>
仅存储一个指针:一个指向对象的指针。
您可以在示例中使用以下代码来查看大小:
println!("{}", mem::size_of::<Box<AnyStorable + 'static>>());
println!("{}", mem::size_of::<Box<Insertable<C>>>());
println!("{}", mem::size_of::<Box<VecStorage<C>>>());
调用
transmute
更改Box
的特征将破坏vtable:不同特征的vtable不兼容。调用
transmute
从对Box<SomeTrait>
的引用更改为对Box<SomeType>
的引用(并且类型恰好是正确的引用)的做法是可行的,因为它只会使用指向对象的第一个指针,而无需考虑特征。胖指针(即带有vtable的数据指针)的内部表示形式是在
TraitObject
中定义的,该属性仅在每晚构建时才可访问。尽管表示不太可能以数据指针不再是第一个指针的方式更改,这将破坏第二个transmute
。TraitObject
的文档也值得阅读。(虽然
transmute
确保传递的类型的大小相等,但是您正在传递对类型的引用-它们始终恰好是一个指针大。它不会检查那些引用所指向的类型。)关于segmentation-fault - 在同时实现两个段错误的类型上从特征A转换为特征B,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/48384622/