几天前,有一个有关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/

10-15 12:18