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 代码时,许多开发人员会进入这样的工作流程
- 编写一些代码。
- 运行
cargo build
以确保它可以编译。 - 根据需要重复步骤 1-2。
- 运行
cargo test
以确保您的测试通过。 - 转到步骤 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 check
,rustc
已学会了发出一种新的文件类型:.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 部分
VecDeque::truncate
VecDeque::resize
String::insert_str
Duration::checked_add
Duration::checked_sub
Duration::checked_div
Duration::checked_mul
str::replacen
str::repeat
SocketAddr::is_ipv4
SocketAddr::is_ipv6
IpAddr::is_ipv4
IpAddr::is_ipv6
Vec::dedup_by
Vec::dedup_by_key
Result::unwrap_or_default
<*const T>::wrapping_offset
<*mut T>::wrapping_offset
CommandExt::creation_flags
File::set_permissions
String::split_off
此外,还对现有函数进行了一些小的改进。例如,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 build
和 cargo 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 做出了贡献。感谢!