手记

Rust 值得吗?

一只不确定的螃蟹

从 JavaScript 到 Rust,三年的历程。

几年前,我放下手头的一切,全身心投入到WebAssembly中。当时,Rust在编译成WebAssembly方面支持最好,功能最全面的WebAssembly运行时也是基于Rust的。Rust是菜单上最好的选择。我跃跃欲试,想看看所有的热度都是关于什么的。

从那时起,我和一些其他很棒的人一起构建了Wick,一个使用WebAssembly作为其核心模块系统的应用程序框架和运行时。

Wick 是我们 Rust 实验的主要目标

经过三年、多次生产部署、一本关于从Node.js迁移到Rust的ebook以及在crates.io上部署了大约100个包之后,我觉得现在是时候分享一些我对Rust的看法了。

一个更好的选择
用更少的维护更多的功能

我非常支持测试驱动开发。我习惯于在像Java和JavaScript这样的语言中进行测试。当我开始用Rust编写测试时,就像在其他语言中一样,但我发现我写的测试是那些无法失败的。一旦你的测试能够运行——也就是说,你的Rust代码能够编译——Rust已经处理了如此多的错误,许多常见的测试用例变得无关紧要。如果你避免使用unsafe {}块和容易引发恐慌的方法如.unwrap(),你从一开始就建立了一个能够默认避开许多问题的基础。

Rust 的借用检查器的严格性、Rust 类型系统的丰富性、函数式模式和库,以及没有“空值”的特性,都导致在测试等地方用较少的努力就能维护更多的代码。我在维护 Wick 项目时,尽管该项目有超过 70,000 行代码,却用了比其他语言所需更少的测试。

当你需要编写测试时,可以轻松地即兴添加它们而无需多想。Rust 的内置测试框架让你可以毫不费力地在代码旁边添加测试。

现在我在其他语言中编写代码更得心应手了

在Rust中编程就像是处在一段情感虐待的关系中。Rust整天对你大喊大叫,经常是因为一些你以前认为完全正常的事情。最终,你习惯了这些发作。它们变得像例行公事。你学会了走钢丝,以避免触发编译器的脾气。就像在现实生活中一样,这些行为改变会永远伴随着你。

情感虐待通常不被认为是一种健康的促进改变的方式,但它仍然会产生影响,带来改变。

当我看到代码行排列不整齐或返回值未被检查时,用其他语言写代码会感到不舒服。现在,当我遇到运行时错误时,也会莫名其妙地感到烦躁。

你什么意思“_done”不是函数?为什么你不告诉我“_done”可能不是一个函数?

Clippy 太棒了!

Clippy 是 Rust 的代码检查工具,但称它为代码检查工具有些贬低了它的作用。在一种能让编译器让你抓狂的语言中,Clippy 更像是一位温柔的朋友,而不仅仅是一个代码检查工具。

Rust 标准库是 极其庞大的。当这么多功能分散在众多细粒度的类型、特征、宏和函数中时,很难找到你认为可能存在的函数。许多 Clippy 规则(例如,[手动的 is_ascii 检查](https://rust-lang.github.io/rust-clippy/master/index.html#/manual_is_ascii_check))会查找标准库方法或类型更适合替代的常见模式。

Clippy 有 数百条规则,这些规则针对性能、可读性和不必要的间接调用进行处理。当可能时,它会经常提供替换代码。

看起来(很快)你将终于能够为项目配置全局lint规则了。直到现在,你还需要通过一些变通的方法来保持项目的lint规则一致。在Wick中,我们使用一个脚本来自动更新内联lint配置,以适用于几十个crate。为此,Rust社区花了多年的时间才找到一个解决方案,这让我们来到了……

不好的地方
你将不得不接受一些不足之处

每次回到上面提到的Clippy问题时,我都怀疑自己的理智。我肯定错了。一定是我漏掉了某个配置。我无法相信,现在仍然无法相信。一定有一种方法可以全局配置lints。我在写这些时,四次-确认 这个 问题,以确保自己没有犯迷糊。那些问题现在已经关闭了,但它们已经开放了多年。

Clippy 很棒,但这种用例只是 Rust 生态系统中的众多例子之一。我经常遇到一些库或工具,它们并没有涵盖我的用例。这在较新的语言或项目中并不罕见。软件需要时间(使用)来成熟。但 Rust 并不是那么新了。Rust 给人的感觉有些不同。

在开源社区中,边缘情况通常是由早期采用者和新用户解决的。他们正是遇到这些边缘情况的人。他们的PR(拉取请求)使项目更适合下一个用户。Rust 已经连续多年被评为“最受喜爱的语言”。它吸引了大量新用户,但并没有显著改善库或工具。相反,它导致了处理特定用例的一次性分叉。我也犯过这样的错误,但这并不是因为我没有努力让 PR 被接受。

我不知道为什么。也许是保持稳定API的压力,加上Rust细粒度的类型系统,使得库的所有者难以进行迭代。如果一个小的改动会导致主要版本的升级,那么很难接受这样的改动。

或者可能是因为为所有人编写适用于所有情况的Rust代码极其困难,人们不想处理这种情况。

Cargo、crates.io 以及如何组织项目

我参照一些其他流行的项目构建了Wick仓库的结构。看起来合理且运行良好,直到它不再奏效。

你可以轻松地使用Cargo构建、测试并使用一个感觉像模块大小的crate。但是将其部署到crates.io上呢?那又是另一回事了。

你不能将包发布到 crates.io,除非 每个 引用的 crate 也都单独发布。这有一定的道理。你不想依赖于一个 crate,而这个 crate 又依赖于仅存在于作者本地文件系统上的包。

然而,许多开发者自然地将大型项目分解为较小的模块,但你不能发布一个包含仅存在于自身内部的子包的父包。甚至,你也不能发布包含本地开发依赖项的包。你必须在发布随机的工具包和重构项目以避免这个问题之间做出选择。这种限制感觉是任意且不必要的。你可以清楚地构建这种结构的项目,只是不能发布它们。

编辑:Ed Page 联系了我,指出你可以使用本地开发依赖项发布,只要你在 Cargo.toml 中不要包含 version

Cargo 的工作空间支持确实非常出色!Cargo 的工作空间在管理大型项目方面提供了比大多数语言更好的体验。但是,它们并不能解决部署问题。事实证明,你可以以十几种不同的方式设置工作空间,但这些方式中没有一种能轻松实现部署。

你可以从大量的工具包中看到这个问题,这些工具包旨在简化发布工作区的过程。每个工具包只适用于部分配置,而设置工作区的“标准方法”仍然没有定论。当我发布Wick时,这通常需要花费一个多小时,结合手动、重复的任务和只能部分工作的工具。

异步

Rust 在语言诞生后增加了异步特性。这种特性感觉像是后来才想到的,表现得也像是后来才想到的,并且常常通过难以理解且难以解决的错误来妨碍你。当你寻找解决方案时,你必须根据不同的运行时及其异步变体进行筛选。想要使用一个异步库?有可能你只能在特定的异步运行时中使用它。

经过二十年的JavaScript和一些Go的经验,这是与Rust最令人沮丧和摩擦的来源。这并不是一个无法克服的问题,但你必须时刻准备好应对异步怪物出现时的情况。在其他语言中,异步几乎是不可见的。

不雅部分
重构可能是一个漫长的过程

Rust 的丰富类型系统既是福也是祸。用 Rust 类型思考是一种梦想。管理 Rust 的类型却可能是一场噩梦。你的数据和函数签名可以有泛型类型、泛型生命周期和 trait 约束。这些约束本身也可以有它们自己的泛型类型和生命周期。有时,你甚至会发现类型约束比实际代码还要多

超越逻辑的约束条件

你还需要在每个地方[实现](https://github.com/bytecodealliance/wasmtime/blob/038ddfeb6699591b5d82546c9b2d5076097bc9ce/cranelift/entity/src/iter.rs#L29-L58)中定义所有的泛型。第一次编写时可能会很繁琐。然而,在重构时,这可能会将一个小改动变成一个连锁反应的混乱。

简单的通用ID被一遍又一遍地重复使用。

当你需要调整14个不同的定义才能迈出一步时,很难取得快速进步。

编辑以回应外部评论:问题不在于表达能力,问题在于没有一种语言或工具解决方案来减少重复。经常需要有相同的约束或引用相同的泛型列表,但没有办法为一个中心定义创建别名或以其他方式引用它。我不确定是否应该有,但这并没有改变重复带来的负担。

最终结论

我爱 Rust。我爱它能做的事情以及它的灵活性。我可以使用同一种语言编写系统级代码、CLI 应用程序、以及 网页服务器。借助 WebAssembly,我可以使用相同的二进制文件在浏览器中运行 LLM 在这里 以及在命令行中运行。这仍然让我感到惊叹。

我爱上了用Rust编写程序的稳定可靠。一旦你学会了欣赏Rust保护你免受哪些问题,就很难再回到其他语言中。我曾短暂地回到Go语言。我很快就又被Go语言开发速度的快捷所吸引。然后我遇到了运行时的恐慌问题,一切又变得支离破碎。

但Rust也有它的缺点。招聘难度大,学习曲线陡峭,且过于僵硬,难以快速迭代。调试内存和性能问题尤其在处理异步代码时非常困难。并非所有库都像其他库那样注重安全代码,开发工具也存在诸多不足。你一开始就会处于劣势,有很多不利因素。如果你能克服这些障碍,你将远远领先于他人。这确实是一个很大的假设。

Rust 对我们来说值得吗?现在还不好说。我们用一个小团队完成了惊人的事情,但也遇到了巨大的障碍。我们还有一些技术原因使 Rust 更加可行。

这对你来说值得吗?如果你需要快速迭代,可能不值得。如果你有一个明确的范围,或者能够承担更多的前期成本?那么绝对值得考虑。你最终会得到坚不可摧的软件。随着WebAssembly的影响力每个月都在增强,编写一次完美的软件并在任何地方重复使用它的前景即将成为现实。

0人推荐
随时随地看视频
慕课网APP