我有一个值的参考,我想用对包装器的引用替换它包装值的结构。替换并使用&包装器类型<Type>
例子:
struct Wrapper<T>(T);
let num = 123;
let x: &i32 = #
let wrapped: &Wrapper<i32> = .. // some conversion
这可能吗? (一个安全解决方案是首选,但没有必要。)
我有一个值的参考,我想用对包装器的引用替换它包装值的结构。替换并使用&包装器类型<Type>
例子:
struct Wrapper<T>(T);
let num = 123;
let x: &i32 = #
let wrapped: &Wrapper<i32> = .. // some conversion
这可能吗? (一个安全解决方案是首选,但没有必要。)
这很容易让你的示例代码的工作:
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。
您不需要任何转换;您可以通过以下方式实现这一目标:
#[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);
问题是,什么时候函数'wrap'声音在什么条件下?我们没有任何文件保证。 – bluss
@bluss我在这个问题上增加了更多信息。看起来它真的不安全,不应该使用它,因此我也删除了确切的代码。我很抱歉在我的答案中提供“不安全”的解决方案。我通常不这样做,但是我在醒来后10分钟写了这个答案。^ _^ –
@bluss我想我已经在一些应用程序中对内存布局做了一些假设(例如,'transmute((u32,u32)):: u64',这对'(,)'的布局做了一个不安全的假设。是否有一个跟踪问题,这种事情(强制布局保证)如何在未来的Rust中得到解决? –