类型转换
让开,类型系统!我们要重新解释这些位,不成功便成仁!尽管本书是关于不安全操作的,但我还是要强调,你应该深入思考是否有其他方法,而不是本节中介绍的操作。这真的是Rust中最可怕的不安全操作。这里的防护措施形同虚设。
mem::transmute<T, U>
接受一个 T
类型的值,并将其重新解释为 U
类型。唯一的限制是 T
和 U
的大小必须相同。使用这个函数导致未定义行为的方式简直令人难以置信。
-
首先,创建任何状态无效的类型实例都会导致无法预测的任意混乱。不要将
3
转换为bool
。即使你永远不对这个bool
做任何事情。就是不要这么做。 -
transmute
具有重载的返回类型。如果你不指定返回类型,它可能会产生一个令人惊讶的类型以满足类型推断。 -
将
&
转换为&mut
是未定义行为。尽管某些用法可能看起来是安全的,但请注意,Rust 优化器可以自由地假设共享引用在其生命周期内不会更改,因此这种转换会违反这些假设。所以- 将
&
转换为&mut
*始终* 是未定义行为。 - 不,你不能这样做。
- 不,你不是特殊的。
- 将
-
转换为没有显式提供的生命周期的引用会产生一个无界生命周期。
-
在不同的复合类型之间进行转换时,你必须确保它们的布局方式相同!如果布局不同,错误的字段将被填充错误的数据,这会让你不高兴,并且也可能导致未定义行为(见上文)。
那么你如何知道布局是否相同呢?对于
repr(C)
类型和repr(transparent)
类型,布局是精确定义的。但对于普通的repr(Rust)
,则不是。即使是同一泛型类型的不同实例也可能具有截然不同的布局。Vec<i32>
和Vec<u32>
*可能* 具有相同的字段顺序,也可能不相同。关于数据布局的保证细节仍在 UCG WG 中制定。
mem::transmute_copy<T, U>
设法比这个更不安全。它从 &T
中复制 size_of<U>
个字节,并将它们解释为 U
。 mem::transmute
的大小检查消失了(因为复制前缀可能是有效的),尽管 U
大于 T
会导致未定义行为。
当然,你也可以使用原始指针转换或 union
来获得这些函数的所有功能,但没有任何 lint 或其他基本健全性检查。原始指针转换和 union
并不能神奇地避免上述规则。