在实现两个段错误的类型上从特征A转换为特征B.

时间:2018-01-22 15:07:59

标签: segmentation-fault rust unsafe

几天前,有一个关于OOP的问题想要使用downcast解决问题。作为一个自我挑战,我尝试使用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
};

运行时会产生段错误:

  

/root/entrypoint.sh:7:5分段错误超时--signal = KILL $ {timeout}&#34; $ @&#34;

然而,当我更换这一行时:

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);

有效。为什么第一行失败而第二行没有?

1 个答案:

答案 0 :(得分:6)

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确保传递类型的大小相等,但您传递的是对类型的引用 - 它们总是只有一个指针大。它不会检查这些引用所指向的类型。 )