Rust 1.16 发布

2017 年 3 月 16 日 · Rust 核心团队

Rust 团队很高兴地宣布 Rust 的最新版本 1.16.0。Rust 是一种专注于安全性、速度和并发性的系统编程语言。

如果您已安装了以前版本的 Rust,则获取 Rust 1.16 非常简单,只需运行:

$ rustup update stable

如果您还没有安装 Rust,您可以从我们网站的相应页面获取 rustup,并查看 GitHub 上1.16.0 的详细发行说明

1.16.0 稳定版的新特性

Rust 1.16 中最大的新增功能是 cargo check。这个新的子命令应在许多情况下加快开发工作流程。

它是做什么的?让我们回顾一下 rustc 如何编译您的代码。编译有许多“阶段”,也就是说,编译器从您的源代码到生成最终二进制文件的过程中会经历许多不同的步骤。您可以通过将 -Z time-passes 传递给 nightly 编译器来查看每个步骤(以及它们花费的时间和内存)

rustc +nightly hello.rs -Z time-passes
time: 0.003; rss: 16MB  parsing
time: 0.000; rss: 16MB  recursion limit
time: 0.000; rss: 16MB  crate injection
time: 0.000; rss: 16MB  plugin loading
time: 0.000; rss: 16MB  plugin registration
time: 0.049; rss: 34MB  expansion
<snip>

有很多步骤。但是,您可以将此过程分为两个大步骤:首先,rustc 执行其所有安全检查,确保您的语法正确等等。其次,一旦它确信一切正常,它会生成您最终执行的实际二进制代码。

事实证明,第二步需要花费大量时间。而且大多数时候,它不是必要的。也就是说,当您处理一些 Rust 代码时,许多开发人员会进入如下工作流程:

  1. 编写一些代码。
  2. 运行 cargo build 以确保其编译。
  3. 根据需要重复 1-2。
  4. 运行 cargo test 以确保您的测试通过。
  5. 跳转至 1。

在第二步中,您永远不会实际运行您的代码。您正在寻求编译器的反馈,而不是实际运行二进制文件。 cargo check 正好支持这种情况:它运行编译器的所有检查,但不生成最终的二进制文件。

那么你实际能获得多少速度提升呢?像大多数性能相关问题一样,答案是“这取决于”。以下是一些非常不科学的基准测试

感谢 cargo diesel
初始构建 134.75 秒 236.78 秒 15.27 秒
初始检查 50.88 秒 148.52 秒 12.81 秒
加速 2.648 1.594 1.192
二次构建 15.97 秒 64.34 秒 13.54 秒
二次检查 2.9 秒 9.29 秒 12.3 秒
加速 5.506 6.925 1.100

“初始”类别是克隆项目后的第一次构建。“二次”类别涉及在 src\lib.rs 的顶部添加一个空行并再次运行命令。这就是为什么初始的更引人注目;它们还涉及对所有依赖项以及 crate 本身执行此操作。如您所见,具有许多依赖项的较大项目会看到很大的改进,但较小的项目看到的改进要小得多。

我们仍在努力总体上改进编译时间,尽管我们目前没有任何特别值得强调的内容。

其他改进

为了支持 cargo checkrustc 学会了发出一种新的文件:.rmeta。此文件将仅包含有关特定 crate 的元数据。 cargo check 需要此文件来处理您的依赖项,以允许编译器检查它们的类型等。它对于 Rust 语言服务器,以及未来可能更多的工具也很有用。

另一个较大的更改是删除了一个长期存在的诊断:consider using an explicit lifetime parameter(考虑使用显式生命周期参数)。每当您的生命周期注释不正确时,并且编译器认为您可能意味着其他内容时,此诊断就会启动。考虑以下代码

use std::str::FromStr;

pub struct Name<'a> {
    name: &'a str,
}

impl<'a> FromStr for Name<'a> {
    type Err = ();

    fn from_str(s: &str) -> Result<Name, ()> {
        Ok(Name { name: s })
    }
}

在这里,Rust 不确定如何处理生命周期;如所编写的,该代码不能保证 s 的生存期与 Name 一样长,这是 Name 有效所必需的。让我们尝试使用 Rust 1.15.1 编译此代码

$ rustc +1.15.1 foo.rs --crate-type=lib
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in generic type due to conflicting requirements
  --> .\foo.rs:10:5
   |
10 |       fn from_str(s: &str) -> Result<Name, ()> {
   |  _____^ starting here...
11 | |         Ok(Name { name: s })
12 | |     }
   | |_____^ ...ending here
   |
help: consider using an explicit lifetime parameter as shown: fn from_str(s: &'a str) -> Result<Name, ()>
  --> .\foo.rs:10:5
   |
10 |       fn from_str(s: &str) -> Result<Name, ()> {
   |  _____^ starting here...
11 | |         Ok(Name { name: s })
12 | |     }
   | |_____^ ...ending here

编译器解释了问题,并给出了有用的建议。所以让我们尝试一下:修改代码以添加 'a,然后再次编译

$ rustc +1.15.1 .\foo.rs --crate-type=lib
error[E0308]: method not compatible with trait
  --> .\foo.rs:10:5
   |
10 |       fn from_str(s: &'a str) -> Result<Name, ()> {
   |  _____^ starting here...
11 | |         Ok(Name { name: s })
12 | |     }
   | |_____^ ...ending here: lifetime mismatch
   |
<snip>
help: consider using an explicit lifetime parameter as shown: fn from_str(s: &'a str) -> Result<Name<'a>, ()>
  --> .\foo.rs:10:5
   |
10 |       fn from_str(s: &'a str) -> Result<Name, ()> {
   |  _____^ starting here...
11 | |         Ok(Name { name: s })
12 | |     }
   | |_____^ ...ending here

它仍然不起作用。该帮助消息实际上没有帮助。它建议在 Name 上添加另一个生命周期。如果我们这样做...

$ rustc +1.15.1 .\foo.rs --crate-type=lib
<snip>
help: consider using an explicit lifetime parameter as shown: fn from_str(s: &'a str) -> Result<Name<'a>, ()>
  --> .\foo.rs:10:5

...那不就是我们已经有的吗,编译器!

这个诊断的本意是好的,但是当它出错时,它非常错误,正如您在这里看到的那样。有时它甚至不会建议有效的 Rust 语法!此外,更高级的 Rust 用户实际上并不需要该建议,但是新的 Rustaceans 会将它们铭记在心,然后被引导到这条错误的道路上。因此,我们决定现在,我们应该完全删除帮助消息。我们可能会在将来将其带回来,但前提是我们可以限制误报。

另外:上述实现是不可能的; Name 需要使用 String,而不是 &str

在其他诊断更改中,以前版本的 Rust 会尝试帮助建议修复拼写错误

let foo = 5;

println!("{}", ffo);

会给出这个错误

error[E0425]: cannot find value `ffo` in this scope
 --> foo.rs:4:20
  |
4 |     println!("{}", ffo);
  |                    ^^^ did you mean `foo`?

但是,这只会发生在某些情况下:有时在局部变量中,以及在结构中的字段中。现在几乎在所有地方都会发生这种情况。当与其他一些相关改进结合使用时,这会在这些类型的诊断中带来显着改进。

有关更多信息,请参阅详细发行说明

库稳定化

此版本中稳定了 21 个新的 API

此外,对现有函数进行了许多小的改进。例如,writeln! 现在有一个单参数形式,就像 println! 一样。这最终只写入一个换行符,但这是一个很好的对称性。

标准库中的所有结构现在都实现了 Debug

当切片一个 &str 时,您会看到更好的错误。例如,此代码

&"abcαβγ"[..4]

是不正确的。它会生成此错误

thread 'str::test_slice_fail_boundary_1' panicked at 'byte index 4 is not
a char boundary; it is inside 'α' (bytes 3..5) of `abcαβγ`'

; 之后的部分是新的。

有关更多信息,请参阅详细发行说明

Cargo 功能

除了 cargo check,Cargo 和 crates.io 还添加了一些新的改进。例如,cargo buildcargo doc 现在采用 --all 标志,用于使用一个命令构建和记录工作空间中的每个 crate。

Cargo 现在有一个 --version --verbose 标志,镜像 rustc

Crates.io 现在可以在您的 crate 页面上显示您的 TravisCI 或 AppVeyor 徽章

此外,Cargo 和 crates.io 都理解类别。与自由形式的关键字不同,类别是经过策划的。此外,关键字用于搜索,但类别不是。换句话说,类别旨在辅助浏览,而关键字旨在辅助搜索。

您可以在此处按类别浏览 crate。

有关更多信息,请参阅详细发行说明

1.16.0 的贡献者

上一个版本,我们介绍了 thanks.rust-lang.org。我们一直在进行一些幕后重构工作,以允许更多项目不仅仅是 Rust 本身;我们希望在下一个版本中介绍这一点。

有 135 位个人为 Rust 1.16 贡献了力量。感谢!