2016-12-27 17 views
2

我有一个值的参考,我想用对包装器的引用替换它包装值的结构。替换并使用&包装器类型<Type>

例子:

struct Wrapper<T>(T); 

let num = 123; 
let x: &i32 = &num; 
let wrapped: &Wrapper<i32> = .. // some conversion 

这可能吗? (一个安全解决方案是首选,但没有必要。)

回答

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(它只是借来的;它只能在上面的代码中工作,因为i32Copy),第二,我们不能返回对局部变量的引用(为我们创建的hidden) )。


要解决这个问题,可以使用unsafe函数std::mem::transmute()。这只是将任何类型解释为另一种类型。

但是等等! unsafe {}的意思是“编译器,相信我!”,但我们应该相信自己?我们作为程序员必须保证Wrapped<T>T具有完全相同的数据布局。那么:是这样吗?

在大多数平台上可能都是如此,但我非常怀疑我们可以保证这一点!当涉及到结构(和单位结构)的数据布局时,Rust似乎没有多少承诺。它可能会对字段进行重新排序(在这种情况下不重要),并可能会添加填充。有关repr(Rust) chapter of the Rustonomicon的更多信息。


总结:像wrap()功能无法安全地实现。因此,应该避免包含类似功能的API。

+4

问题是,什么时候函数'wrap'声音在什么条件下?我们没有任何文件保证。 – bluss

+0

@bluss我在这个问题上增加了更多信息。看起来它真的不安全,不应该使用它,因此我也删除了确切的代码。我很抱歉在我的答案中提供“不安全”的解决方案。我通常不这样做,但是我在醒来后10分钟写了这个答案。^ _^ –

+0

@bluss我想我已经在一些应用程序中对内存布局做了一些假设(例如,'transmute((u32,u32)):: u64',这对'(,)'的布局做了一个不安全的假设。是否有一个跟踪问题,这种事情(强制布局保证)如何在未来的Rust中得到解决? –

1

您不需要任何转换;您可以通过以下方式实现这一目标:

#[derive(Debug)] 
struct Wrapper(i32); 

let num = 123; // the value 
let x = &num; // the reference to the value 
let wrapped: Wrapper = Wrapper(*x); // now you can refer to &wrapped 

println!("{:?}", wrapped);