什么是Rust及其为何如此受欢迎?

更新于 2026-01-15

Jake Goulding 2020-01-20

Rust连续四年被Stack Overflow评为最受欢迎的编程语言,这表明许多有机会使用Rust的人都爱上了它。然而,大约97%未使用过Rust的受访者可能会疑惑,“Rust到底有什么特别之处?”

简短的答案是,Rust解决了许多其他语言中存在的痛点,提供了一个坚实的进展,且缺点有限。

我将展示Rust为其他编程语言用户提供了什么,以及当前生态系统的样子。当然,在Rust的世界里并非一切都是一帆风顺的,因此也会谈到它的不足之处。

动态类型语言用户的视角

关于偏好动态类型与静态类型的程序员之间的争论可能还会持续几十年,但是静态类型的优点难以忽视。看看像TypeScript这样的语言的崛起或Python的类型提示功能就知道了,当人们对于当今大型代码库中的动态类型现状感到沮丧时,这些变化应运而生。静态类型语言允许对数据及其行为设置编译器检查约束,减轻认知负担和误解。

但这并不是说所有静态类型系统都是等同的。许多静态类型的语言都有一个大大的星号:它们允许NULL的概念存在。这意味着任何值都可能是它所说的,或者什么都不是,实际上为每种类型创造了第二种可能性。就像Haskell和其他一些现代编程语言一样,Rust使用可选类型编码这种可能性,并且编译器要求你处理None的情况。这样可以防止出现可怕的运行时错误(例如TypeError: Cannot read property 'foo' of null),而是将其提升到编译时错误,可以在用户看到之前解决。

fn greet_user(name: Option) {
    match name {
        Some(name) => println!("Hello there, {}!", name),
        None => println!("Well howdy, stranger!"),
    }
}

Rust的静态类型系统在鼓励长期可维护性的同时尽可能不干扰程序员。某些静态类型语言给程序员带来了沉重的负担,需要他们多次重复变量的类型,这妨碍了可读性和重构。其他静态类型语言允许全程序类型推断。虽然在初始开发过程中很方便,但这减少了编译器在类型不再匹配时提供有用错误信息的能力。Rust从这两种风格中学习,要求顶级项如函数参数和常量具有显式类型,同时允许在函数体内进行类型推断。

fn simple_math(val: i32) -> i32 {
    let twice = val * 2;
    twice - 1
}

来自垃圾回收语言的视角

使用系统级编程语言的最大好处之一就是能够控制底层细节。

Rust让你可以选择将数据存储在栈上还是堆上,并在编译时确定何时内存不再需要并可以清理。这允许更高效的内存使用以及更高效的内存访问。Tilde是一家早期在Skylight产品中采用Rust的企业,发现通过用惯用的Rust重写某些Java HTTP端点,他们的内存使用量从5GiB减少到了50MiB。当你向云提供商支付更多内存或额外节点的溢价时,这样的节省会迅速累积起来。

由于不需要垃圾收集器持续运行,Rust项目非常适合通过外部函数接口被其他编程语言作为库使用。这让现有项目可以用快速的Rust代码替换性能关键部分,同时避免了与其他系统编程语言相关的内存安全风险。有些项目甚至已经逐步用Rust进行了重写。

直接访问硬件和内存,使得Rust成为嵌入式和裸机开发的理想语言。你可以编写极其低级别的代码,如操作系统内核或微控制器应用程序。Rust的核心类型和函数以及可复用的库代码在这些特别具有挑战性的环境中表现突出。

来自其他系统级编程语言的视角

对于许多人来说,Rust主要被视为C或C++等其他系统级编程语言的替代品。相比这些语言,Rust能提供的最大优势在于借用检查器。这是编译器的一部分,负责确保引用不会超出其所指向的数据的生命期,有助于消除由内存不安全引起的整个类别的bug。

不像许多现有的系统级编程语言,Rust并不强制你花费所有时间深陷于繁琐的细节之中。Rust努力实现尽可能多的零成本抽象——这些抽象与手工编写的代码同样高效。在这个例子中,我们展示了迭代器如何用于简洁地创建一个包含前十个平方数的向量。

let squares: Vec = (0..10).map(|i| i * i).collect();

当安全的Rust无法表达某些概念时,你可以使用unsafe Rust。这解锁了一些额外的功能,但作为交换,程序员需要确保代码确实是安全的。这段不安全的代码然后可以被包裹在保证所有使用该抽象都是安全的更高层次的抽象中。

使用不安全代码应该是一个经过计算的决定,因为正确使用它需要像在其他任何你需要避免未定义行为的语言中那样思考和小心。最小化不安全代码是减少因内存不安全导致的段错误和漏洞可能性的最佳方式。

系统级编程语言隐含的预期是它们将永远存在。尽管一些现代开发并不需要那么长的时间跨度,但许多企业希望知道他们的基础代码库在未来可预见的时间里仍然可用。Rust认识到了这一点,并围绕向后兼容性和稳定性做出了有意识的设计决策;这是一种设计用于未来40年的语言。

Rust生态系统

Rust体验不仅仅是一个语言规范和编译器;创建和维护生产质量软件的许多方面都被视为一等公民。可以通过rustup安装和管理多个并发的Rust工具链。Rust安装自带Cargo,这是一个命令行工具,用于管理依赖、运行测试、生成文档等。由于默认情况下依赖、测试和文档都是可用的,因此它们的使用非常普遍。crates.io是共享和发现Rust库的社区站点。发布到crates.io的任何库都会在其文档构建后发布到docs.rs。

除了内置工具外,Rust社区还创建了大量的开发工具。基准测试、模糊测试和基于属性的测试都非常容易访问并在项目中广泛使用。Clippy提供了额外的编译器lints,rustfmt提供了自动格式化。IDE支持健康且每天都在变得更加有能力。

超越技术层面,Rust拥有一个充满活力、友好的社区。有几个官方和非官方的渠道供人们获取帮助,如聊天室、用户论坛、Rust subreddit,当然还有Stack Overflow的问题和答案及聊天室。Rust有一套行为准则,并由一个很棒的 moderation团队执行,以确保官方空间的欢迎氛围,大多数非官方空间也遵守类似的规定。

离线方面,Rust在全球范围内有多个会议,如RustConf、Rust Belt Rust、RustFest、Rust Latam、RustCon Asia等。

并非一切都是美好的

Rust强大的类型系统和对内存安全的强调——所有这些都是在编译时强制执行的——意味着在编译代码时遇到错误是非常常见的。对于那些不习惯如此严格的编程语言的程序员来说,这可能会令人沮丧。然而,Rust开发者花了很多时间改进错误消息,以确保它们清晰且可操作。不要让眼睛在阅读Rust错误时变得模糊!

经常会听到有人抱怨他们在“对抗借用检查器”。虽然这些错误可能令人气馁,但重要的是要认识到每个标识的位置都有可能引入在没有执行相同检查的语言中出现的bug和潜在漏洞。

fn no_mutable_aliasing() {
    let mut name = String::from("Vivian");
    let nickname = &name[..3];
    name.clear();
    println!("Hello there, {}!", nickname);
}

错误示例解释省略...

原型解决方案在Rust中可能具有挑战性,因为其静态类型特性以及Rust要求覆盖100%的条件,而不是仅仅99%。即使程序员还不知道成功路径应该做什么,边缘情况也必须有适用的代码。在早期开发阶段,这些边缘情况通常可以通过使程序崩溃来解决,然后在稍后的阶段添加严格的错误处理。这与Ruby等语言的工作流程不同,在Ruby中,开发者经常尝试在REPL中试用代码,然后直接将其移到原型中而不考虑错误案例。

Rust仍然是相对较新的,这意味着一些所需的库可能还不可用。另一方面,这也意味着有很多肥沃的土地可以用来开发这些需要的库,也许还可以利用计算机科学领域的最新发展。正因为如此,Rust的一些库,比如regex crate,是跨任何语言的最佳选择。

虽然Rust对稳定性和向后兼容性有着强烈的承诺,但这并不意味着语言已经定型。特定问题可能无法访问会使表达更加简单或甚至可能表达的语言特性。例如,Rust已经有超过三年的异步future,但语言本身的稳定async/await支持只有几个月的历史。

Rust编译器构建在LLVM之上,这意味着目标平台的数量将比C或C++少。

加入我们吧! 无论你现在喜欢哪种编程语言,肯定有一些关于Rust的东西会让你兴奋或感兴趣。这是我和其他人如此喜爱Rust的一些原因,还有更多。如果你正在寻找项目的额外结构、更快或更有效的代码,或者是能够更快速、更安全地编写高性能代码的能力,现在是时候看看Rust是否会成为你下一个最喜欢的编程语言了!