我有一个值的引用,我想用一个包含该值的包装器结构的引用替换它。
示例:
struct Wrapper<T>(T);
let num = 123;
let x: &i32 = #
let wrapped: &Wrapper<i32> = .. // some conversion
这可能吗? (安全解决方案是首选,但不是必需的。)
答案 0 :(得分:2)
使示例代码很容易实现:
let wrapped: &Wrapper<i32> = &Wrapper(*x); // type annotation optional
来自C ++,您可能认为这是疯狂的不安全因为我们引用了一个临时(左侧的表达式)。但在这种情况下,Rust只会将此临时表达式的结果保存在堆栈中。上面的代码相当于:
let hidden = Wrapper(*x);
let wrapped: &Wrapper = &hidden;
到目前为止一直很好,但是当我们想要返回此引用时会出现问题。例如:
fn wrap<T>(t: &T) -> &Wrapped<T> {
&Wrapped(*t)
}
我们这里有两个问题。首先,我们不能离开t
(它只是借用;它只在上面的代码中起作用,因为i32
是Copy
),其次,我们不能返回一个局部变量(为我们创建的hidden
)。
要解决此问题,您可以使用unsafe
函数std::mem::transmute()
。这只是将任何类型解释为另一种类型。
但是等等! unsafe {}
表示“编译器,相信我!”,但我们应该相信自己吗?作为程序员,我们必须保证Wrapped<T>
和T
具有完全相同的数据布局。那么:就是这样吗?
在大多数平台上可能都是这样,但我非常怀疑我们可以保证这一点!在结构(和单元结构)的数据布局方面,Rust似乎没有多少承诺。它可能会重新排序字段(在这种情况下不重要)并可能添加填充。 repr(Rust) chapter of the Rustonomicon中有更多相关信息。
总结:wrap()
等功能无法安全实施。因此,应避免使用包含此类功能的API。
答案 1 :(得分:1)
您不需要任何转换;你可以通过以下方式实现这一目标:
#[derive(Debug)]
struct Wrapper(i32);
let num = 123; // the value
let x = # // the reference to the value
let wrapped: Wrapper = Wrapper(*x); // now you can refer to &wrapped
println!("{:?}", wrapped);