转码

让开,类型系统!我们要重新解释这些位,否则就死!尽管这本书都是关于做不安全的事情,但我真的怎么强调都不为过,你应该深入思考,找到除了本节介绍的操作之外的“其他方法”。这真的是,真的,是你在 Rust 中能做的最可怕的不安全的事情。这里的护栏是牙线。

mem::transmute<T, U> 接受一个类型为 T 的值,并将其重新解释为类型 U。唯一的限制是 TU 被验证为具有相同的大小。用这种方法导致未定义行为的方式令人难以置信。

  • 首先,用无效状态创建*任何*类型的实例都会导致无法真正预测的任意混乱。不要将 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> 字节,并将它们解释为 Umem::transmute 所具有的尺寸检查消失了(因为它复制出一个前缀可能是有效的),尽管 U 大于 T 是未定义行为。

当然,你也可以使用原始指针转换或 union 来获得这些函数的所有功能,但没有任何 lint 或其他基本健全性检查。原始指针转换和 union 并不能神奇地避免上述规则。