在实现两个段错误的类型上从特征 A 转换为特征 B
Transmuting from trait A to trait B on type that implements both segfaults
前几天有个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: line 7: 5 Segmentation fault timeout --signal=KILL ${timeout} "$@"
然而,当我替换这一行时:
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>
存储了两个指针:一个指向对象,一个指向虚表。 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
的特征会破坏虚表:不同特征的虚表不兼容。
调用 transmute
将对 Box<SomeTrait>
的引用更改为对 Box<SomeType>
的引用(并且类型恰好是正确的)恰好有效,因为它只会使用指向对象的第一个指针并忘记特征。
胖指针(即带有 vtable 的数据指针)的内部表示在 TraitObject
中定义,它只能在夜间构建中访问。尽管不太可能表示形式可能会改变,数据指针不再是第一个指针,这会破坏第二个 transmute
.
TraitObject
的文档也值得一读。
(虽然 transmute
确保传递的类型的大小相等,但您传递的是对类型的引用 - 它总是恰好是一个指针大。它不检查这些引用指向的类型到。)
前几天有个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: line 7: 5 Segmentation fault timeout --signal=KILL ${timeout} "$@"
然而,当我替换这一行时:
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>
存储了两个指针:一个指向对象,一个指向虚表。 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
的特征会破坏虚表:不同特征的虚表不兼容。
调用 transmute
将对 Box<SomeTrait>
的引用更改为对 Box<SomeType>
的引用(并且类型恰好是正确的)恰好有效,因为它只会使用指向对象的第一个指针并忘记特征。
胖指针(即带有 vtable 的数据指针)的内部表示在 TraitObject
中定义,它只能在夜间构建中访问。尽管不太可能表示形式可能会改变,数据指针不再是第一个指针,这会破坏第二个 transmute
.
TraitObject
的文档也值得一读。
(虽然 transmute
确保传递的类型的大小相等,但您传递的是对类型的引用 - 它总是恰好是一个指针大。它不检查这些引用指向的类型到。)