在过去的一段时间里,“用Rust重写”的潮流席卷了整个开发领域。作一颗正在冉冉升起的新星,Rust不仅承诺了更好的内存安全保障、更高的技术稳定性,还能够兼顾开发与执行效率。这也让越来越多的系统开发者开始转向这门面向未来的编程语言。
这不,就在最近,又一款知名开源项目Ockam放弃了系统内数万行的C语言代码,并最终用Rust实现了对整个项目的重写。在完成这项庞大的工程后,Ockam的创始人Mrinal Wadhwa也在一篇博客中分享了自己带领团队从C转向Rust的心路历程。
1、C语言:迷人的陷阱
作为一款开源开发者工具,Ockam在GitHub上拥有3.3k Stars,其核心功能是帮助用户构建可信的动态数据,为用户的应用添加端到端的加密和认证通信;保障应用能够获得端到端的数据完整性、真实性和机密性。
研发团队希望Ockam能够在任何环境中运行,包括受约束的边缘设备或是强大的云服务器。除此之外,Ockam的另一个目标则是可以在任何类型的应用程序中使用,无论应用程序使用什么语言构建。
这样的诉求使得C语言成为了一个构建Ockam项目的明显候选者——它能够被绝大多数设备编译,并且所有流行语言都可以通过某种形式的接口调用C库。在这样的情况下,Ockam能够为所有其他语言提供惯用的包装器。
团队的想法是将以通信为中心的协议核心从硬件行为中分离,并为想要支持的硬件提供可插拔的适配器。在这样的思路下,研发人员在最初的版本中将项目的Ockam的核心实现为了一个C库,并用其他语言包装器对库进行了包装。
2、一触即溃的安全问题
然而基于Ockam核心所实现的C库虽然满足了项目四处运行的需求,但由于C语言内存管理的薄弱,团队所实现的C库中许多与加密相关的代码容易存在漏洞,一个微小的失误就会导致系统变得不安全。
这与Ockam项目的目标简直背道而驰,为了将这些问题隐藏起来,并提供易于正确使用的开发者界面。团队开始尝试使用C语言构建安全简单的接口。但在多次迭代中,开发人员渐渐发现自己不得不掌握大量关于协议状态和状态转换的细节,并且即便非常小心,代码中也总会出现难以察觉的漏洞。
3、Elixir:并不够好的继任者
面对无比痛苦的迭代工作,始终无法解决内存安全问题的Ockam决定放弃C语言,并为项目寻找更适合的继任者——彼时的他们将视线投到了以Erlang为基础的Elixir语言之上。
Elixir程序运行在Erlang虚拟机BEAM上,BEAM提供了Erlang过程。Erlang流程是轻量级有状态并发参与者。由于actors可以在维护内部状态的同时并发运行,所以运行一个并发的有状态协议堆栈Ockam传输+Ockam路由+Ockam安全通道非常容易。
图片
但令人遗憾的是,Elixir天然为支持高负载项目而生,无法像C语言一样在小型或受限的计算机上拥有良好的运行表现,此外生态并不够成熟的Elixir也无法成为某些特定语言管理包装器的良好选择。
4、Rust:令人兴奋的探索之旅
在经历了接二连三的失败后,Ockam团队意识到他们的需求核心是在保障安全性的前提下实现轻量级的actors,但无论是C语言还是Elixir都无法做到完美适配。从这时起,Wadhwa开始带领团队研究Rust,并很快发现了这门语言独特的魅力。
与C语言调用约定(calling convention)的兼容
Rust库能够导出与C调用约定兼容的接口。这意味着任何静态/动态链接或调用C库中函数的语言都能够以完全相同的方式调用Rust库中的函数。并且由于大多数语言都支持C中的原生函数,所以他们也支持Rust中的原生函数,从包装器的角度来看,Rust和C几乎没有任何区别。
支持多目标
Rust使用LLVM编译,这意味着它可以针对非常多的计算机。这个集合不像C用GCC和专用的GCC分支所能达到的那样大,但仍然是一个非常大的子集。随着新的LLVM目标和Rust中潜在的GCC支持的增长,这能够满足Ockam到处运行的需求。
强大的内存安全性
Rust的内存安全功能消除了释放后使用、双重释放、溢出、越界访问等许多常见错误的可能性。根据此前的调查,这些错误会导致C或C++库中60-70%的严重漏洞。Rust在编译时提供了这种安全性,使其在编写需要高性能、受限环境中运行并高度安全的代码时具有很大的优势。
Async/await
让研发团队坚信Rust与Ockam能成为天作之和的最后一个原因是Rust中的async/await。Ockam需要轻量级的actors来创建简单而安全的协议栈接口。async/await意味着在tokio和async-std这样的项目中已经完成了大量创建actor的工作,团队能够很轻松地在此基础上构建Ockam的actor实现。
5、最终的天作之合
基于Rust的async/await无论在大型计算机还是微型计算机上运行,都可以向用户呈现完全相同的界面。位于Ockam Workers上的协议接口也可以在任何不同地方运行的情况下呈现完全相同的简单接口。
最终,在Wadhwa的带领下,Ockam放弃了系统内的数万行C语言代码,并利用一段时间完成了向Rust的全面转型。在经历了重写后的多次稳定性迭代后,如今任何用户都能够使用重获新生的Ockam包,在任何设备上利用简单函数调用创建他们想要的端到端的加密和相互认证的安全通道。
更重要的是,依托于Rust的内存安全优势,这个函数调用的背后能够隐藏所有潜在的复杂性和漏洞风险。
6、值得期待的未来
在Rust日渐趋于宏大的叙事中,Ockam的故事其实只是一个缩影。对如今绝大多数受困与内存安全问题的项目而言,使用Rust或基于Rust进行重构已不再是一项实验或赌注,而是切实可行且足够可靠的解决方案。
在Ockam之前,微软已于4月宣布,出于对内存安全的考虑,将使用18万行Rust代码重写核心Windows库;Python Web框架Flask的作者Armin也在Python 2023发展趋势的文章中提出了将Rust融入Python项目和工具的想法;Ruff创始人在成立新公司时,同样宣称了要在未来通过Rust彻底改变Python生态。
更值得欣喜的是,Rust的进化如今仍在继续,根据其发布的Rust 2024路线中显示,官方团队将在未来继续在降低学习门槛、壮大生态连接等方面努力,帮助Rust尽快撕下被人诟病的“曲高和寡”的标签,让其尽快在更多场景中发挥价值。对此,Rust的未来,的确值得我们每个人的期待。