- 历史:2010 年 Graydon Hoare 在 Mozilla 年度峰会上介绍 Rust 时,其开篇幻灯片称该项目为“过去的技术来拯救未来”,这通常被理解为对 Rust 影响的致敬,包括 Scheme(1975)、StandardML(1983)和 Cyclone(2001)。实际上,Rust 的根源可追溯到 1379 年,著名书商 Nicholas Flamel 翻译了 Abremelin 法师的神秘著作,据说他将铅变成了金并获得了永生。2012 年 7 月,Brian Anderson 向 Rust 添加了
transmute
函数,它是一个unsafe
函数,可将任意类型的源值转换为任意类型的值。 危险:
- 位有效性:源值必须是目标类型的位有效实例,否则会导致未定义行为。
- 对齐:如果涉及引用,除了位有效性,还需要考虑引用对齐。不同类型在 Rust 中有对齐要求,违反对齐要求可能导致未定义行为。
- 生命周期:有引用的地方就有生命周期,使用
transmute
可能会意外地导致悬空引用,这是未定义行为。 - 安全不变量:需要注意违反自己的安全不变量,例如绕过结构体的构造函数使用
transmute
可能导致潜在的不安全。
为什么使用
transmute
?案例研究:- 传统数据包解析:传统的 UDP 数据包解析器可能会按值读取数据包的各个字段,代码简洁但不够高效。
- 零拷贝数据包解析:通过重新定义
UdpPacketHeader
的布局使其与 UDP 数据包头的有线布局匹配,并使用transmute
将&[u8; N]
转换为&UdpPacketHeader
,可以减少代码行数并优化为少量机器代码。但零拷贝解析仍然非常危险,容易出错。 - 安全转换包:为了解决零拷贝解析的危险,Rust 社区创建了各种安全抽象的包,如
bytemuck
和zerocopy
。这些包通过定义标记 trait 来提供安全的抽象,例如FromBytes
trait,为各种类型提供基本实现,并通过 proc 宏派生简化用户使用。 - Fuchsia 网络栈:Google 的 Fuchsia 操作系统的工程师使用
zerocopy
包构建了一个几乎无错误的网络栈,证明了安全抽象的有效性。
- 引入安全转换项目:
zerocopy
背后有近 14000 行的不安全代码和安全注释,不够理想。因此创建了项目安全转换,希望提供新的、更安全的替代方案来替代mem::transmute
和指针转换。 - 炼金术理论:为了解决任意类型的可转换性问题,开发了类型炼金术理论,将类型视为有限自动机。布局的类型是一个有限自动机,其中每条边表示一个有效性约束,从开始到结束的每条边路径表示类型的特定可能值。这种表示方式可以优雅地扩展,并且适合对可转换性进行多项式时间分析。
- 为炼金术士创建安全护目镜:实现了
TransmuteFrom
trait,它是一个unsafe
标记 trait,由编译器自动为可转换的任何两种类型实现。使用该 trait 可以将以前的不安全示例转换为编译错误,从而提供有效的安全保护。同时还构建了一个名为ASSUME
的逃生舱,允许程序员告诉编译器哪些安全属性由程序员自己负责确保,从而实现更安全的转换。 - 使用
TransmuteFrom
:该 trait 为进行转换的人提供了有效的安全护目镜,特别是对于抽象转换。对于一次性转换,ASSUME
可以明确需要在安全注释中编写的内容。此外,它还为转换的抽象提供了基础,例如可以将zerocopy
的FromBytes
trait 的安全负担从程序员转移到编译器。 未来展望:
- 支持 DSTs:需要支持动态大小类型,以便完全支持
zerocopy
等包。 - 可失败的转换:希望有一个互补的
TryTransmuteTrait
,可以自动生成必要的运行时检查,进一步减少用户错误导致未定义行为的风险。 - 关键优化:需要实现各种优化来降低使用
TransmuteFrom
的编译时间成本。 - 可移植性和稳定性:需要解决 Rust 中的布局可移植性问题,并研究将类型布局和转换的 SemVer 稳定性反映到类型系统中的可能性。
- RFC 和稳定化:可能首先关注
TransmuteFrom
的落地,然后在包生态系统中迭代解决相关问题,并开始起草相关的 RFC。
- 支持 DSTs:需要支持动态大小类型,以便完全支持
总之,安全转换是 Rust 发展的一个重要方向,通过各种技术和工具的不断改进,有望提高 Rust 程序的安全性和性能。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。