在此代码中,我将获取一个向量,创建一个struct实例,并将其添加到带框的向量中:
trait T {}
struct X {}
impl T for X {}
fn add_inst(vec: &mut Vec<Box<T>>) -> &X {
let x = X {};
vec.push(Box::new(x));
// Ugly, unsafe hack I made
unsafe { std::mem::transmute(&**vec.last().unwrap()) }
}
很明显,它使用了mem::transmute
,这让我觉得这不是正确的方法。难道这是唯一的方法吗?
此外,尽管它在Rust 1.32中编译,但在Rust 1.34中失败:
error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
--> src/lib.rs:10:14
|
10 | unsafe { std::mem::transmute(&**vec.last().unwrap()) }
| ^^^^^^^^^^^^^^^^^^^
|
= note: source type: `&dyn T` (128 bits)
= note: target type: `&X` (64 bits)
答案 0 :(得分:3)
我认为这段代码是安全的:
fn add_inst(vec: &mut Vec<Box<dyn T>>) -> &X {
let x = X {};
let b = Box::new(x);
let ptr = &*b as *const X;
vec.push(b);
unsafe { &*ptr }
}
诀窍是将原始指针保存到*const X
,然后再将其转换为Box<dyn T>
。然后,您可以将其转换回引用,然后再从函数中将其返回。
这是安全的,因为装箱的值永远不会移动(当然,除非将其从Box
中移出),所以ptr
在将b
转换为Box<dyn T>
的过程中仍然存在
答案 1 :(得分:3)
您的“丑陋骇客”实际上是完全不正确且不安全的。您很遗憾Rust 1.32没有报告该错误,但值得庆幸的是Rust 1.34不会报告该错误。
存储带框的值时,将创建一个薄指针。这会占用平台的本地整数大小(例如,在32位x86上为32位,在64位x86上为64位,等等):
+----------+
| pointer |
| (0x1000) |
+----------+
存储盒装特征对象时,将创建一个 fat指针。它包含指向数据的相同指针和对 vtable 的引用。该指针的大小为两个本机整数:
+----------+----------+
| pointer | vtable |
| (0x1000) | (0xBEEF) |
+----------+----------+
通过尝试从trait对象到引用的转换,您正在丢失这些指针之一,但是未定义哪个指针。无法保证首先出现:数据指针或vtable。
一种解决方案将使用std::raw::TraitObject
,但这是不稳定的,因为胖指针的布局仍然悬而未决。
我建议不使用unsafe
代码的解决方案是使用Any
:
use std::any::Any;
trait T: Any {}
struct X {}
impl T for X {}
fn add_inst(vec: &mut Vec<Box<dyn T>>) -> &X {
let x = X {};
vec.push(Box::new(x));
let l = vec.last().unwrap();
Any::downcast_ref(l).unwrap()
}
如果您不能/不想使用Any
,I've been told,则将特征对象指针转换为指向具体类型的指针将仅保留数据指针。不幸的是,我找不到为此的正式参考,这意味着尽管凭经验可以使用,但我无法完全保证该代码:
fn add_inst(vec: &mut Vec<Box<dyn T>>) -> &X {
let x = X {};
vec.push(Box::new(x));
let last: &dyn T = &**vec.last().unwrap();
// I copied this code from Stack Overflow without reading
// it and it may not actually be safe.
unsafe {
let trait_obj_ptr = last as *const dyn T;
let value_ptr = trait_obj_ptr as *const X;
&*value_ptr
}
}
另请参阅: