我为什么反对使用Rust?

2020 年 9 月 27 日

我为什么反对使用Rust?

本文最初发布于 matklad 的个人博客,由 InfoQ 中文站翻译并分享。

我最近读到一篇批评 Rust 的文章,虽然它提出了一堆好观点,但我不喜欢它——这篇文章很容易引起争论。一般来说,我觉得我不能推荐一篇批评 Rust 的文章。这是件有点令人丢脸的事——直面缺点很重要,而且,批评批评者不努力或知识欠缺会错过一些实际上很好的论点。

以下是我反对 Rust 的理由。

不是所有的编程都是系统编程

Rust 是一种系统编程语言。它可以精确控制数据布局和代码的运行时行为,为你提供了最大的性能和灵活性。与其他系统编程语言不同,它还提供了内存安全性——有 Bug 的程序会以定义良好的方式终止,而不是触发未定义的行为(可能是安全敏感的行为)。

然而,在许多(大多数)情况下,我们不需要最好的性能或对硬件资源的控制。在这种情况下,像 Kotlin 或 Go 这样的现代化托管语言提供了不错的速度、令人羡慕的执行时间,并且由于使用垃圾收集器进行动态内存管理而具有内存安全性。

复杂性

程序员的时间非常宝贵,如果你选择Rust,那么你得准备好花一些时间来学习一些技巧。Rust 社区投入了大量的时间来编写高质量的教程,但是Rust 语言非常大。即使Rust 实现能够为你提供价值,你也可能无法投入资源来提高这门语言的专业技能。

Rust 为改善控制所付出的代价是选择麻烦:

复制代码
struct Foo { bar: Bar }
struct Foo<'a> { bar: &'a Bar }
struct Foo<'a> { bar: &'a mut Bar }
struct Foo { bar: Box<Bar> }
struct Foo { bar: Rc<Bar> }
struct Foo { bar: Arc<Bar> }

在 Kotlin 中,你编写类class Foo(val bar: Bar),然后继续解决你的业务问题。在 Rust 中,你需要做出一些选择,其中一些非常重要,以至于需要专门的语法。

所有这些复杂性的存在都是有原因的——我们不知道如何创建一个更简单的内存安全的低级语言。但并不是所有的任务都需要低级语言来解决。

延伸阅读:为什么 C++ 避免了“瓦萨”号沉没的命运?( https://www.youtube.com/watch?v=ltCgzYcpFUI )

编译时间

编译时间是所有事情的倍增器。如果一门语言运行速度慢但编译速度快,那么用它编写的程序也可以运行得更快,因为程序员将有更多的时间来做优化!

Rust在泛型困境中有意选择了慢速编译器。这并不是世界末日(最终的运行时性能改进是真的),但它确实意味着,在较大的项目中,你必须竭尽全力才能获得合理的构建时间。

rustc实现了可能是生产编译器中最先进的增量编译算法,但这感觉有点像是在与语言编译模型作斗争。

与C++ 不同,Rust 的构建并不是并行的;并行量受依赖图中关键路径的长度限制。如果你有40 多个core 需要编译,这就显出来了。

Rust 还缺乏与 pimpl 惯用语类似的东西,这意味着更改一个 crate 需要重新编译(而不仅仅是重新链接)其所有反向依赖关系。

成熟度

Rust 才五岁,绝对是一门年轻的语言。尽管它的未来看起来很光明,我还是会把更多的钱押在“C 语言 10 年后仍将存在”上,而不是“Rust 语言 10 年后仍将存在”上(参见林迪效应)。如果你编写的软件要用几十年,那么你应该认真考虑选择新技术伴随的风险。(但是请记住,在90 年代,为银行软件选择Java 而不是Cobol 是正确的选择)。

Rust 只有一个完整的实现——rustc编译器。最先进的替代实现mrustc有意省略了许多静态安全检查。目前,rustc只支持一个可用于生产的后端——LLVM。因此,它对 CPU 架构的支持比 C 更窄,C 有 GCC 实现和许多特定于供应商的专有编译器。

最后,Rust 缺乏官方规范。语言参考还在编写中,文档中也没有详细提供所有的实现细节。

替代语言

在系统编程领域,除了Rust 外,还有其他语言,比较出名的有C、C++ 和Ada。

现代C++ 为提高安全性提供了工具指南。甚至有人提议,建立一个类似Rust lifetime 的机制

!与 Rust 不同,使用这些工具并不能保证不出现内存安全问题。但是,如果你已经维护了大量的 C++ 代码,那么还是检查下,这些代码是否遵循了最佳实践,并借助 sanitizers 检测安全问题。这很难,但显然比用另一种语言重写要容易得多!

如果你使用 C,则可以使用正式的方法来证明没有未定义的行为,或者只是详尽地测试一切。

Ada 是内存安全的,如果你不使用动态内存(永远不调用free)。

Rust 是成本 / 安全曲线上一个有趣的点,但不是唯一的点!

工 具

Rust 工具有好有坏。基线工具、编译器和构建系统( cargo ),经常被认为是最好的工具。

但是,举例来说,它缺少一些运行时相关的工具(最明显的是堆分析)——如果没有运行时,就很难反映程序的运行时状况!此外,尽管 IDE 支持很不错,但它的可靠性还远远达不到 Java 级别。在如今的 Rust 中,自动对数百万行程序进行复杂的重构是不可能的。

集 成

无论 Rust 的承诺是什么,如今的系统编程世界都是使用 C 语言,由 C 和 C++ 所占据,这就是事实。Rust 有意避免模仿这些语言——它不使用 C++ 风格的类或 C ABI。

这意味着,世界之间的整合需要明确的桥梁。这不是无缝的。它们unsafe,总会有点成本,并且需要在语言之间保持同步。虽然分段集成的承诺已经实现,工具也赶上了进度,但在此过程中会出现偶发的复杂性。

一个特别的问题是,Cargo 固执的世界观(这对于纯Rust 项目来说是件好事),但可能会使它更难与更大的构建系统集成。

性 能

“使用LLVM”并不是所有性能问题的通用解决方案。虽然我不知道在规模较大时,C++ 和Rust 基准测试的性能对比,但是不难想出一组场景,在这些场景中,相对于C++,Rust 还有一些性能问题没有解决。

最大的问题可能是,Rust 的move 语义是基于值的(机器代码级的 memcpy)。相反,C++ 语义使用特殊的引用,你可以从中窃取数据(机器代码级的指针)。理论上,编译器应该能够识别副本链;实践中,情况通常并非如此:#57077 。一个相关的问题是,没有placement new——Rust 有时需要从栈复制字节/ 将字节复制到栈,而C++ 可以在适当的位置构造。

有点可笑的是,Rust 的默认ABI(为使其尽可能高效而不稳定)有时比C 更糟糕:#26494

最后,虽然从理论上讲,由于Rust 具有丰富得多的aliasing 信息,其代码应该更高效,但启用与aliasing 相关的优化将触发LLVM Bug 和编译错误:#54878

但是,重申一下,这些都是刻意挑选的例子,可能有失偏颇。例如, std::unique_ptr有一个性能问题,而Rust 的 Box则没有。

一个潜在的更大的问题是,Rust 的定义时检查泛型比 C++ 表达性差。因此,一些用于提高性能的 C++ 模板技巧无法在 Rust 中使用好的语法进行表达。

不安全的含义

对于 Rust 而言,一个比 ownership&borrowing 更核心的概念可能是unsafe边界。将所有的危险操作都放在unsafe块和函数的后面,并为它们提供一个安全的高层接口,就有可能创建一个同时满足以下两点的系统:

  1. 健壮(非unsafe代码不会导致未定义的行为);

  2. 模块化(不同的unsafe块可以单独检查)。

很明显,在实践中,这种保证是有效的:对Rust 代码进行模糊测试,发现隐藏的panics,避免缓冲区溢出。

但理论上,前景并不乐观。

首先,缺少Rust 内存模型定义,就不可能正式检查给定的不安全块是否有效。对于“rustc 所做的或可能依赖的事情”和正在开发中的运行时验证器,都只有非正式的定义,而实际的模型是不断变化的。因此,某些地方可能存在一些 unsafe代码,这些代码今天还能正常工作,明天就可能被声明失效,并在明年被新的编译器优化破坏掉。

其次,还有一种观点认为,unsafe块实际上并不是模块化的。总之,功能足够强大的unsafe块可以扩展该语言。两个这样的扩展单独使用可能没有问题,但如果同时使用,则会导致未定义的行为:观测性等价和不安全代码

最后,编译器中还有公开的 Bug

下面是我特意从清单中删除的一些内容:

  • 经济因素(“招聘 Rust 程序员更困难”)——我觉得“成熟度”一节抓住了问题的本质,它不能简化为鸡和蛋的问题。

  • 依赖关系(“stdlib 太小 / 所有东西都有太多的依赖”)——考虑到 Cargo 以及语言的相关部分都很不错,我个人并不认为这是一个问题。

  • 动态链接(“Rust 应该有稳定的 ABI”)——我不认为这是一个强有力的论点。从根本上来说,单态化(Monomorphization)与动态链接是不兼容的,如果需要的话,还有 C ABI。我确实认为这里的情况可以改善,但我不认为这种改善是 Rust 特有的

感兴趣的读者可以点击这里: https://www.reddit.com/r/rust/comments/iwij5i/blog_post_why_not_rust/ ,查看关于本文的讨论。

查看英文原文:

https://matklad.github.io/2020/09/20/why-not-rust.html

2020 年 9 月 27 日 14:30 2162
用户头像
陈思 InfoQ编辑

发布了 510 篇内容, 共 178.4 次阅读, 收获喜欢 942 次。

关注

评论 7 条评论

发布
用户头像
无话可说
2020 年 09 月 29 日 15:00
回复
用户头像
写这篇文章的人垃圾,翻译的也是。你不懂别瞎bb,你所谓不看好,所反对的,并不会影响人家语言的繁荣昌盛。请IT社区不要渲染这种毒鸡汤,挑起没必要的编程语言竞争战斗。谢谢!
2020 年 09 月 28 日 09:55
回复
……翻译得确实烂,但是你连原文也一起喷,说明你其实并没有认真看过原文。这个作者实际上是 Rust 的支持者而不是反对者。他写这篇文章的原因是,他看到了另一篇批评 Rust 的文章,但他觉得那篇文章没批评到点子上,于是他写了这篇文章告诉大家 Rust 真正的缺点在哪里。
2020 年 09 月 30 日 03:41
回复
其实他的批评不无道理,一味的扣帽子解决不了争议,相反,大家把合理的问题说出来才是有益的
2020 年 10 月 02 日 00:10
回复
用户头像
垃圾
2020 年 09 月 28 日 09:53
回复
用户头像
保守派,好多大公司都在试用Rust了。
2020 年 09 月 27 日 14:55
回复
你得看场景,目前MS,Apple在用的都是非常特定的场景,不是那种体量的企业确实有必要斟酌一下必要性的问题
2020 年 10 月 02 日 00:11
回复
没有更多评论了
发现更多内容

架构师第一周总结

suke

极客大学架构师训练营

Week 01 作业:食堂就餐卡系统设计

鱼_XueTr

Homework-食堂就餐卡系统设计

River Tree

架构设计 Homework

sed命令基础

飞翔

Linux

week1《作业二:根据当周学习情况,完成一篇学习总结》

任鑫

架构师训练营-week1-学习总结

暖丶冬

第二课 以图服人

Geek_bobo

学习总结-架构师训练营-第一周

走过路过飞过

食堂就餐卡系统设计

史慧君

食堂就餐卡系统设计

chinsun1

架构文档

架构师训练营第一周心得

努力努力再努力m

极客大学架构师训练营

食堂就餐卡系统设计

week1作业

数字

食堂就餐卡系统设计

呱呱

极客大学架构师训练营 作业

架构师0期 | 食堂就餐卡系统架构设计文档

刁架构

极客大学架构师训练营

架构师和架构

拈香(曾德政)

架构师 极客大学架构师训练营

第一周总结

王志祥

极客大学架构师训练营

week1-食堂就餐卡系统

张健

食堂就餐卡

架构师训练营-学习笔记-第一周

superman

极客大学架构师训练营 UML学习

食堂就餐卡系统设计

小遵

week1-学习总结

张健

食堂就餐卡系统设计

泛岁月的涟漪

架构师训练营 - 学习总结 - 第一周

桔子

架构师-第一课总结

free[啤酒]

架构师学习第一周作业

云峰

食堂就餐卡系统设计

拈香(曾德政)

架构设计 极客大学架构师训练营

【总结】架构师要做什么

缺省模式

食堂就餐系统架构设计(训练营第一课)

看山是山

极客大学架构师训练营 UML 食堂就餐系统

架构师训练营第1周作业

无名氏

【架构训练营】第一期

云064

架构师训练营-Week 01 学习总结

华乐彬

架构 架构师 极客大学架构师训练营 学习总结

万亿规模下美团命名服务的演进与业务赋能

万亿规模下美团命名服务的演进与业务赋能

我为什么反对使用Rust?-InfoQ