宣布 Rust 1.16

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

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

如果您已安装了先前版本的 Rust,获取 Rust 1.16 非常简单,只需

$ rustup update stable

如果您还没有安装,可以从我们网站上的相应页面获取 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。

在步骤 2 中,您实际上从未运行过您的代码。您正在寻找来自编译器的反馈,而不是实际运行二进制文件。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 做出了贡献。感谢!