几天前,有一个关于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);
有效。为什么第一行失败而第二行没有?
答案 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
确保传递类型的大小相等,但您传递的是对类型的引用 - 它们总是只有一个指针大。它不会检查这些引用所指向的类型。 )