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