Rust 五周年

2020年5月15日 · Rust 核心团队

考虑到世界上的种种状况,即使你忘记了今天是我们发布 1.0 五周年的日子,也是情有可原的!过去五年里,Rust 发生了很大变化,所以我们想回顾一下语言稳定版发布以来所有贡献者的工作。

Rust 是一种通用编程语言,赋予每个人构建可靠且高效软件的能力。Rust 可以构建运行在技术栈的任何地方,无论是操作系统的内核还是你的下一个 Web 应用。它完全由开放和多元化的个人社区构建,这些主要由无私奉献时间与专业知识的志愿者组成,他们帮助 Rust 成为现在的样子。

1.0 版本以来的主要变化

2015

1.2 — 并行代码生成:编译时间改进是 Rust 每个版本的重要主题,很难想象曾有一段短暂的时间 Rust 完全没有并行代码生成。

1.3 — 《Rustonomicon》:我们首次发布了精彩的《Rustonomicon》,这本书深入探讨了 Unsafe Rust 及其周边主题,已成为任何希望学习和理解该语言最困难方面之一的宝贵资源。

1.4 — Windows MSVC 一级支持:第一个一级平台升级是将使用 Microsoft Visual C++ 工具链 (MSVC) 的 64 位 Windows 原生支持。在 1.4 之前,你需要安装 MinGW(第三方 GNU 环境)才能使用和编译 Rust 程序。Rust 的 Windows 支持是过去五年最大的改进之一。最近,微软宣布了其 WinRT API 官方 Rust 支持的公开预览版!现在构建高质量的原生和跨平台应用比以往任何时候都更容易。

1.5 — Cargo Install:Cargo 增加了构建 Rust 二进制文件的能力,并在此基础上利用 Cargo 已有的插件支持,催生了一个包含社区喜爱并依赖的大量应用、实用工具和开发者工具的生态系统。如今 Cargo 的许多命令最初都是社区在 crates.io 上构建和共享的插件!

2016

1.6 — Libcore:libcore 是标准库的一个子集,仅包含不需要分配或操作系统级别功能的 API。libcore 的稳定化带来了无需分配或操作系统依赖即可编译 Rust 的能力,这是 Rust 支持嵌入式系统开发的第一步重要进展。

1.10 — C ABI 动态库:cdylib crate 类型允许 Rust 被编译为 C 动态库,使你能够在任何支持 C ABI 的系统中嵌入 Rust 项目。Rust 在用户中最大的成功故事之一是能够用 Rust 编写系统中一个小型关键部分,并将其无缝集成到更大的代码库中,现在这比以往任何时候都更容易。

1.12 — Cargo Workspaces:Workspaces 允许你组织多个 Rust 项目并共享同一个 lockfile。Workspaces 在构建大型多 crate 项目中发挥了不可估量的作用。

1.13 — Try 运算符:第一个重要的语法添加是 ? 或 "Try" 运算符。该运算符允许你轻松地通过调用栈传播错误。以前你必须使用 try! 宏,每次遇到结果时都需要包装整个表达式,现在你可以轻松地使用 ? 链式调用方法。

try!(try!(expression).method()); // Old
expression?.method()?;           // New

1.14 — Rustup 1.0:Rustup 是 Rust 的工具链管理器,它允许你无缝使用任何版本的 Rust 或其任何工具。最初是一个不起眼的 shell 脚本,现在维护者们亲切地称之为“嵌合体”(chimera)。能够在 Linux、macOS、Windows 和数十个目标平台上提供一流的编译器版本管理,这在五年前简直是天方夜谭。

2017

1.15 — Derive 过程宏:Derive 宏允许你创建强大而广泛的强类型 API,而无需大量的样板代码。这是第一个你可以在稳定版上使用像 serdediesel 的 derive 宏库的版本。

1.17 — Rustbuild:对语言贡献者来说最大的改进之一是将我们的构建系统从最初基于 make 的系统迁移到使用 cargo。这使得成员和新人都可以更容易地构建和贡献 rust-lang/rust 项目。

1.20 — Associated Constants:以前常量只能与模块关联。在 1.20 中,我们稳定了结构体、枚举和重要的 trait 上的关联常量。这使得更容易为 API 中的类型添加一组丰富的预设值,例如常见的 IP 地址或有意义的数字。

2018

1.24 — 增量编译:在 1.24 之前,当你在库中进行更改时,rustc 必须重新编译所有代码。现在 rustc 在尽可能多地进行缓存方面变得更加智能,只需重新生成所需的部分。

1.26 — impl Trait:添加 impl Trait 使你能够获得富有表现力的动态 API,同时拥有静态分派的优势和性能。

1.28 — 全局分配器:以前你只能使用 Rust 提供的分配器。通过全局分配器 API,你现在可以根据自己的需求定制分配器。这是创建 alloc 库的重要一步,alloc 库是标准库的另一个子集,仅包含标准库中需要分配器的部分,例如 VecString。现在在各种系统上使用标准库的更多部分比以往任何时候都更容易。

1.31 — 2018 版:2018 版的发布无疑是我们自 1.0 以来最大的版本,在完全向后兼容的方式下,添加了一系列语法更改和 Rust 编写方面的改进,允许使用不同版本构建的库无缝地协同工作。

  • 非词法生命周期
  • 模块系统改进
  • Const 函数
  • Rustfmt 1.0
  • Clippy 1.0 是 Rust 用于捕获常见错误的 linter。Clippy 使确保你的代码不仅安全而且正确变得容易得多。
  • Rustfix:随着所有语法变化,我们知道我们希望提供工具来使过渡尽可能容易。现在当 Rust 的语法需要更改时,只需一个 cargo fix 即可解决。

2019

1.34 — Alternative Crate Registries:随着 Rust 在生产环境中越来越多地使用,在非公共空间托管和使用项目有着更大的需求,虽然 cargo 一直允许远程 git 依赖,但使用 Alternative Registries,你的组织可以轻松构建和共享自己的 crate 注册表,这些 crate 可以在你的项目中像在 crates.io 上一样使用。

1.39 — Async/Await:用于处理 Futures 的 async/await 关键字的稳定是使 Rust 中的异步编程成为一流公民的主要里程碑之一。即使在其发布六个月后,Rust 中的异步编程也已发展成为一个多元化且高性能的生态系统。

2020

1.42 — Subslice patterns:虽然不是最大的变化,但添加 .. (rest) 模式是一个期待已久的体验改进功能,它极大地提高了切片模式匹配的表达能力。

错误诊断

我们没有太多提及的一点是 Rust 的错误消息和诊断自 1.0 以来改进了多少。现在回过头看旧的错误消息,感觉像是在看另一种语言。

我们重点介绍了一些最能展示我们在向用户指出错误位置、重要的是帮助他们理解为什么不工作以及教他们如何修复方面取得了多大进步的例子。

第一个例子 (Traits)
use std::io::Write;

fn trait_obj(w: &Write) {
    generic(w);
}

fn generic<W: Write>(_w: &W) {}
1.2.0 错误消息
   Compiling error-messages v0.1.0 (file:///Users/usr/src/rust/error-messages)
src/lib.rs:6:5: 6:12 error: the trait `core::marker::Sized` is not implemented for the type `std::io::Write` [E0277]
src/lib.rs:6     generic(w);
                 ^~~~~~~
src/lib.rs:6:5: 6:12 note: `std::io::Write` does not have a constant size known at compile-time
src/lib.rs:6     generic(w);
                 ^~~~~~~
error: aborting due to previous error
Could not compile `error-messages`.

To learn more, run the command again with --verbose.

A terminal screenshot of the 1.2.0 error message.

1.43.0 错误消息
   Compiling error-messages v0.1.0 (/Users/ep/src/rust/error-messages)
error[E0277]: the size for values of type `dyn std::io::Write` cannot be known at compilation time
 --> src/lib.rs:6:13
  |
6 |     generic(w);
  |             ^ doesn't have a size known at compile-time
...
9 | fn generic<W: Write>(_w: &W) {}
  |    ------- -       - help: consider relaxing the implicit `Sized` restriction: `+  ?Sized`
  |            |
  |            required by this bound in `generic`
  |
  = help: the trait `std::marker::Sized` is not implemented for `dyn std::io::Write`
  = note: to learn more, visit <https://doc.rust-lang.net.cn/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>

error: aborting due to previous error

For more information about this error, try `rustc --explain E0277`.
error: could not compile `error-messages`.

To learn more, run the command again with --verbose.

A terminal screenshot of the 1.43.0 error message.

第二个例子 (帮助)
fn main() {
    let s = "".to_owned();
    println!("{:?}", s.find("".to_owned()));
}
1.2.0 错误消息
   Compiling error-messages v0.1.0 (file:///Users/ep/src/rust/error-messages)
src/lib.rs:3:24: 3:43 error: the trait `core::ops::FnMut<(char,)>` is not implemented for the type `collections::string::String` [E0277]
src/lib.rs:3     println!("{:?}", s.find("".to_owned()));
                                    ^~~~~~~~~~~~~~~~~~~
note: in expansion of format_args!
<std macros>:2:25: 2:56 note: expansion site
<std macros>:1:1: 2:62 note: in expansion of print!
<std macros>:3:1: 3:54 note: expansion site
<std macros>:1:1: 3:58 note: in expansion of println!
src/lib.rs:3:5: 3:45 note: expansion site
src/lib.rs:3:24: 3:43 error: the trait `core::ops::FnOnce<(char,)>` is not implemented for the type `collections::string::String` [E0277]
src/lib.rs:3     println!("{:?}", s.find("".to_owned()));
                                    ^~~~~~~~~~~~~~~~~~~
note: in expansion of format_args!
<std macros>:2:25: 2:56 note: expansion site
<std macros>:1:1: 2:62 note: in expansion of print!
<std macros>:3:1: 3:54 note: expansion site
<std macros>:1:1: 3:58 note: in expansion of println!
src/lib.rs:3:5: 3:45 note: expansion site
error: aborting due to 2 previous errors
Could not compile `error-messages`.

To learn more, run the command again with --verbose.

A terminal screenshot of the 1.2.0 error message.

1.43.0 错误消息
   Compiling error-messages v0.1.0 (/Users/ep/src/rust/error-messages)
error[E0277]: expected a `std::ops::FnMut<(char,)>` closure, found `std::string::String`
 --> src/lib.rs:3:29
  |
3 |     println!("{:?}", s.find("".to_owned()));
  |                             ^^^^^^^^^^^^^
  |                             |
  |                             expected an implementor of trait `std::str::pattern::Pattern<'_>`
  |                             help: consider borrowing here: `&"".to_owned()`
  |
  = note: the trait bound `std::string::String: std::str::pattern::Pattern<'_>` is not satisfied
  = note: required because of the requirements on the impl of `std::str::pattern::Pattern<'_>` for `std::string::String`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0277`.
error: could not compile `error-messages`.

To learn more, run the command again with --verbose.

A terminal screenshot of the 1.43.0 error message.

第三个例子 (借用检查器)
fn main() {
    let mut x = 7;
    let y = &mut x;

    println!("{} {}", x, y);
}
1.2.0 错误消息
   Compiling error-messages v0.1.0 (file:///Users/ep/src/rust/error-messages)
src/lib.rs:5:23: 5:24 error: cannot borrow `x` as immutable because it is also borrowed as mutable
src/lib.rs:5     println!("{} {}", x, y);
                                   ^
note: in expansion of format_args!
<std macros>:2:25: 2:56 note: expansion site
<std macros>:1:1: 2:62 note: in expansion of print!
<std macros>:3:1: 3:54 note: expansion site
<std macros>:1:1: 3:58 note: in expansion of println!
src/lib.rs:5:5: 5:29 note: expansion site
src/lib.rs:3:18: 3:19 note: previous borrow of `x` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `x` until the borrow ends
src/lib.rs:3     let y = &mut x;
                              ^
src/lib.rs:6:2: 6:2 note: previous borrow ends here
src/lib.rs:1 fn main() {
src/lib.rs:2     let mut x = 7;
src/lib.rs:3     let y = &mut x;
src/lib.rs:4
src/lib.rs:5     println!("{} {}", x, y);
src/lib.rs:6 }
             ^
error: aborting due to previous error
Could not compile `error-messages`.

To learn more, run the command again with --verbose.

A terminal screenshot of the 1.2.0 error message.

1.43.0 错误消息
   Compiling error-messages v0.1.0 (/Users/ep/src/rust/error-messages)
error[E0502]: cannot borrow `x` as immutable because it is also borrowed as mutable
 --> src/lib.rs:5:23
  |
3 |     let y = &mut x;
  |             ------ mutable borrow occurs here
4 |
5 |     println!("{} {}", x, y);
  |                       ^  - mutable borrow later used here
  |                       |
  |                       immutable borrow occurs here

error: aborting due to previous error

For more information about this error, try `rustc --explain E0502`.
error: could not compile `error-messages`.

To learn more, run the command again with --verbose.

A terminal screenshot of the 1.43.0 error message.

团队引言

当然,我们无法涵盖所有已发生的更改。所以我们联系并询问了我们的一些团队,他们最引以为豪的更改是什么。

对于 rustdoc 来说,重要的事情有:

  • 针对 blanket implementations 自动生成的文档
  • 搜索本身及其优化(最后一个是将它转换为 JSON)
  • 更准确地测试文档代码块的可能性:“compile_fail, should_panic, allow_fail”
  • Doc tests 现在生成为它们自己的独立二进制文件。

— Guillaume Gomez (rustdoc)

Rust 现在有了基本的 IDE 支持!在 IntelliJ Rust、RLS 和 rust-analyzer 之间,我觉得大多数用户应该能为他们选择的编辑器找到“还不错”的体验。五年前,“编写 Rust”意味着使用老式的 Vim/Emacs 配置。

— Aleksey Kladov (IDE 和编辑器)

对我来说,那就是:为流行的嵌入式架构添加一流的支持,并建立一个蓬勃发展的生态系统,让使用 Rust 进行微控制器开发成为一种轻松、安全且有趣的体验。

— Daniel Egger (嵌入式 WG)

发布团队大约在 2018 年初才成立,但即使在那段时间里,我们仅在 rust-lang/rust 中就提交了约 40000 次 commit,而稳定版中没有出现重大回归。

考虑到我们改进编译器和标准库的速度有多快,我认为这确实令人印象深刻(当然发布团队并非唯一的贡献者)。总的来说,我发现发布团队在处理 issue 跟踪器上不断增加的流量、提交的 PR 等方面做得非常出色。

— Mark Rousskov (发布)

在过去 3 年里,我们成功地将 Miri 从一个实验性解释器转变为探索语言设计和在真实代码中发现 bug 的实用工具——这是 PL 理论和实践的完美结合。在理论方面,我们有 Stacked Borrows,这是迄今为止最具体的 Rust 别名模型提案。在实践方面,虽然最初只有少数关键库由我们自己在 Miri 中检查,但最近我们看到越来越多的人使用 Miri 来在自己的 crate 和依赖中发现并修复 bug,并且贡献者也越来越多地改进 Miri,例如添加对文件系统访问、unwinding 和并发的支持。

— Ralf Jung (Miri)

如果我必须选择一件我最引以为豪的事情,那就是非词法生命周期 (NLL) 的工作。这不仅是因为我认为它极大地提高了 Rust 的可用性,还因为我们通过组建 NLL 工作组来实现它的方式。这个工作组带来了许多优秀的贡献者,其中许多人至今仍在为编译器贡献力量。这是开源的最好体现!

— Niko Matsakis (语言)

社区

正如语言在过去五年中发生了很大变化和发展一样,其社区也随之壮大。有如此多用 Rust 编写的优秀项目,Rust 在生产环境中的存在也呈指数级增长。我们想分享一些关于 Rust 增长了多少的统计数据。

  • 自 1.0 版本发布以来,Rust 在过去四年的 Stack Overflow 开发者调查中,每年都被评选为“最受欢迎的编程语言”
  • 仅今年一年,我们就提供了超过 2.25 PB(1 PB = 1,000 TB)不同版本的编译器、工具和文档!
  • 同期,我们在 crates.io 上为约 18 亿次请求提供了超过 170 TB 的 crate,月流量是去年的两倍。

当 Rust 达到 1.0 版本时,在生产环境中使用它的公司屈指可数。如今,它被数百家科技公司使用,其中一些最大的科技公司如苹果、亚马逊、Dropbox、Facebook、谷歌和微软选择在他们的项目中将 Rust 用于其性能、可靠性和生产力。

结论

显然,我们无法涵盖自 2015 年以来 Rust 发生的所有变化或改进。你最喜欢的变化或新的 Rust 项目是什么?欢迎在我们的 Discourse 论坛上发布你的答案和讨论。

最后,我们要感谢所有为 Rust 做出贡献的人,无论你贡献了新功能还是修复了错别字,你的工作都使 Rust 成为今天如此出色的项目。我们迫不及待地想看到 Rust 及其社区如何继续成长和变化,并期待在未来十年里看到你们用 Rust 构建什么!