作者 | Nabil Elqatib
译者 | 平川
策划 | 刘燕
本文最初发布于 Nabil Elqatib 的个人博客。
接触 Rust 开发快两年了。我觉得,回顾下自己在这个过程中的一些感想和汲取的经验教训,应该会很有趣。
下图是我第一次向一个 Rust 存储库提交代码。虽然时间是 2021 年 1 月,但彼时我接触 Rust 已经有几个周了。
在 2020 年 12 月找到一份新工作之前,我早就听说过 Rust。以我嵌入式开发的视角看,我认为 Rust 是一种现代而强大的语言,最终可能成为 C/C++的合法继承者。我隐隐有一种担优,我可能会错过一个新时代的开始。
剧透:事实证明,Rust 并没有取代 C/C++,不过它正在稳步增长。它越来越受开发人员欢迎,甚至谷歌也对它怀有浓厚的兴趣。
首先需要声明一下:我没有正式学习过 Rust。相反,我只是通过开发、阅读代码和来回翻阅文档来学习。回想起来,我认为这并不是一个好的做法。我坚信,在实际使用这门语言进行开发之前,必须花一些时间,利用官方提供的学习资料来快速地理解这门语言、它的哲学和生态系统。
我记得,Rust 是作为一种强类型语言向人“推销”的,它承诺提供强大的工具,通过在编译期间跟踪对象生命周期和所有引用变量的作用域,来防止内存安全 Bug(见下一小节)。Borrow-checker 出场!
在使用静态类型和Rc以及其他微妙的语言构件处理多个 crate 时,这个看起来非常简单的想法(Rust 文档中关于这部分的内容非常全面)变成了一场噩梦。当时,我很难理解这个新概念,最容易钻的空子是不断地克隆变量,这样做不好(特别是在处理大型数据结构时),而且还有一个副作用,就是使我花了更多的时间才最终掌握它。
幸运的是,编译器提供了非常有用的提示和文档链接,我必须说,它们非常有用。
这是一个非常普遍的误解,是我最近与一位非 Rust 工程师同事聊天时了解到的。在他看来,Rust 程序因为运行时内存越界故障而恐慌(panic)是不可想象的。遗憾的是,Cargo 编译器并不是一种包治百病的灵丹妙药,显然,欺骗它成功编译只会在运行时失败的程序很容易。下面这个例子使用了一种非常常见的 Rust 数据结构:
let mut v = vec![];v.push(0);v.clear();let _ = v[0]; // panics
或者更棘手:
let mut v = Vec::new();#[cfg(target_os = "windows")]v.push("a");let _ = v[0]; // panics
在编译时检测越界访问需要对代码进行更深入的分析,这可以显著降低编译速度(在我看来,编译时间已经太长了)。
这一节和我最近观察到的一个行为有关。我们的团队发现,在特定条件下,生产环境中的其中一个 crate(依赖项的)依赖项开始出现恐慌。简单来说就是,当与rustls-tls-native-roots特性一起使用时,如果系统证书损坏,那么特定版本的reqwest会引发错误和恐慌。
这让我很惊讶,因为它使得处理依赖关系多了几分风险。
尽管现在大多数 crate 都是开源的,但人们也不能为了评估使用“风险”而把所有源代码都检查一遍。而且,大多数 crate 的文档比较糟糕,也没法提供很好的支持。有一个类似于 cargo tree 的工具,可以分析项目的依赖关系,让开发人员可以概览容易出现恐慌的 crate,会非常有帮助。
习惯上,快速处理这个问题的一个替代方法是重写 Rust 的panic_handler,但很遗憾,这只有在#![no_std] 项目中才有可能。
说到来自嵌入式世界的no_std,这是我特别感兴趣的一个点。我还没有机会为内存有限的低端设备和外设编写 Rust 代码。尽管这现在不是我关心的问题,但 Rust 二进制文件的开销确实非常大。
我读过一些关于这个话题的博客和论文,特别是 Jon Gjengset 的视频,非常有趣,因为他们概要介绍了一个真实的实践过程。但我想说的是,对于内存有限的目标设备,Rust 要成为 C 语言的有力竞争者还有很长的路要走。我觉得,在#![no_std] 领域,Rust 还需要找到一种方法来提供可行的恐慌处理程序库,不再依赖开箱即用的格式化函数,因为它们非常消耗内存。James Munns的这篇文章让我大开眼界。
这点也给我留下了深刻的印象。
我参与过一个项目,包含 900K+行与 Rust 互操作的 C 代码。这并没有多难,因为 Rust 让这个过程变得非常简单,而且,因为有许多 crate 和示例,这个用例似乎也已经比较完善。借助 FFI 机制,为外部代码编写 Rust 绑定也相对简单,我不能说所有语言,但 C 语言家族(C/ C++ /Objective-C)都得到了很好的支持。
这不是说没有多少工作要做了,因为你还需要编写一些“管道”代码来把所有东西串联起来,这有代价,但我认为相当低。此外,Rust 坚持自己的哲学,要求将可疑的底层代码声明为不安全的,这一点很好(实际上,基本上所有 FFI 函数都是不安全的,因为 Rust 无法控制用另一种语言编写的代码)。
最后说一个积极的方面。Rust 是一种非常丰富的语言,有可能让系统编程取得巨大的进步。严格的所有权和借阅有助于确保数据访问安全高效。它的现代语法和设计使得开发人员更容易理解和使用这门编程语言的软件模式和最新范式。此外,虽然说的不多,但 Rust 在线程并发运行方面做得非常好,可以保证不出现竞态条件及类似问题。
我真的很喜欢 Rust 开发。如果在 2020 年甚至 2022 年要选择一种新的软件开发语言,那么我一定会毫不犹豫地选择 Rust。我看到了它的巨大潜力,希望接下来的几个月里,它可以获得嵌入式系统世界的更多关注。最近发布的 Rust Linux 内核模块显示出了其广阔的前景和光明的未来。
就我个人而言,我希望更多地参与塑造 Rust 的未来,为它的软件做出贡献,同时不断学习它的概念及那些纷繁芜杂之处。
声明:本文为 InfoQ 翻译,未经许可禁止转载。
原文链接:
https://n-eq.github.io/blog/2022/11/01/rust-fiddling-2-years