类型转换

让开,类型系统!我们要重新解释这些位,不成功便成仁!尽管本书是关于不安全操作的,但我还是要强调,你应该深入思考是否有其他方法,而不是本节中介绍的操作。这真的是Rust中最可怕的不安全操作。这里的防护措施形同虚设。

mem::transmute<T, U> 接受一个 T 类型的值,并将其重新解释为 U 类型。唯一的限制是 TU 的大小必须相同。使用这个函数导致未定义行为的方式简直令人难以置信。

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

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