Rust 团队很高兴宣布 Rust 的新版本 1.31.0 以及“Rust 2018”。Rust 是一种编程语言,它使每个人都能构建可靠高效的软件。
如果您之前通过 rustup 安装了 Rust 的旧版本,获取 Rust 1.31.0 就像以下操作一样简单:
$ rustup update stable
如果您还没有安装,您可以从我们网站上的相应页面获取 rustup
,并查看 GitHub 上1.31.0 的详细发布说明。
1.31.0 稳定版中的内容
Rust 1.31 可能是自 Rust 1.0 以来最令人兴奋的版本!此版本包含“Rust 2018”的第一个迭代,但不仅仅如此!这将是一篇很长的文章,因此这里有一个目录:
Rust 2018
我们在 3 月份和7 月份首次写了关于 Rust 2018 的文章。有关 Rust 2018 的“为什么”的更多背景信息,请阅读这些文章;发布公告中有很多内容要介绍,因此我们将重点介绍这里的“什么”。Mozilla Hacks 上也有一篇文章!
简而言之,Rust 2018 是一个机会,可以将我们过去三年所做的所有工作整合在一起,并创建一个连贯的软件包。这不仅仅是语言特性,还包括:
- 工具(IDE 支持、
rustfmt
、Clippy) - 文档
- 领域工作组工作
- 一个新网站
我们将在本文中介绍所有这些内容以及更多内容。
让我们使用 Cargo 创建一个新项目
$ cargo new foo
以下是 Cargo.toml
的内容
[package]
name = "foo"
version = "0.1.0"
authors = ["Your Name <[email protected]>"]
edition = "2018"
[dependencies]
在 [package]
下添加了一个新键:edition
。请注意,它已设置为 2018
。您也可以将其设置为 2015
,如果键不存在,则为默认值。
通过使用 Rust 2018,可以解锁一些在 Rust 2015 中不允许的新功能。
重要的是要注意,每个包都可以处于 2015 或 2018 模式,并且它们可以无缝地协同工作。您的 2018 项目可以使用 2015 依赖项,而 2015 项目可以使用 2018 依赖项。这确保我们不会分裂生态系统,并且所有这些新事物都是可选的,保留了对现有代码的兼容性。此外,当您选择将 Rust 2015 代码迁移到 Rust 2018 时,可以通过 cargo fix
自动进行更改。
您可能会问,什么样的新功能?首先,除非功能需要与 2015 的功能不兼容,否则功能将添加到 Rust 2015 中。因此,大多数语言在任何地方都可用。您可以查看版本指南,以检查每个功能的最小 rustc
版本以及版本要求。但是,我们想在这里提一下几个重要的功能:非词法生命周期,以及一些模块系统改进。
非词法生命周期
如果您在过去几年中一直关注 Rust 的开发,您可能听说过“NLL”或“非词法生命周期”这个词。这是行话,但它可以直观地翻译成更简单的术语:借用检查器变得更智能了,现在它接受了一些以前拒绝的有效代码。考虑以下示例
fn main() {
let mut x = 5;
let y = &x;
let z = &mut x;
}
在旧版本的 Rust 中,这是一个编译时错误
error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable
--> src/main.rs:5:18
|
4 | let y = &x;
| - immutable borrow occurs here
5 | let z = &mut x;
| ^ mutable borrow occurs here
6 | }
| - immutable borrow ends here
这是因为生命周期遵循“词法作用域”;也就是说,从 y
的借用被认为一直保持到 y
在 main
结束时超出作用域,即使我们不再使用 y
。这段代码没问题,但借用检查器无法处理它。
今天,这段代码可以正常编译。
如果我们确实使用了 y
,例如这样
fn main() {
let mut x = 5;
let y = &x;
let z = &mut x;
println!("y: {}", y);
}
旧版本的 Rust 会给你这个错误
error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable
--> src/main.rs:5:18
|
4 | let y = &x;
| - immutable borrow occurs here
5 | let z = &mut x;
| ^ mutable borrow occurs here
...
8 | }
| - immutable borrow ends here
使用 Rust 2018,此错误得到了改善
error[E0502]: cannot borrow `x` as mutable because it is also borrowed as immutable
--> src/main.rs:5:13
|
4 | let y = &x;
| -- immutable borrow occurs here
5 | let z = &mut x;
| ^^^^^^ mutable borrow occurs here
6 |
7 | println!("y: {}", y);
| - borrow later used here
它没有指向 y
超出作用域的位置,而是显示了冲突的借用发生的位置。这使得这类错误更容易调试。
在 Rust 1.31 中,此功能是 Rust 2018 独有的。我们计划在稍后将其移植到 Rust 2015。
模块系统更改
模块系统对于初学 Rust 的人来说可能是一场斗争。当然,每个人都有自己的需要时间掌握的东西,但它对许多人来说如此令人困惑有一个根本原因:虽然有一些简单一致的规则定义了模块系统,但它们的后果可能感觉不一致、违反直觉和神秘。
因此,Rust 的 2018 版本对路径的工作方式进行了一些更改,但它们最终简化了模块系统,使其更清楚地了解发生了什么。
以下是简要总结
- 在几乎所有情况下都不再需要
extern crate
。 - 您可以使用
use
导入宏,而不是#[macro_use]
属性。 - 绝对路径以 crate 名称开头,其中关键字
crate
指的是当前 crate。 foo.rs
和foo/
子目录可以共存;当将子模块放在子目录中时,不再需要mod.rs
。
这样说来,这些可能看起来像是任意的新规则,但总体而言,思维模型现在已经大大简化了。
这里有很多细节,因此请阅读版本指南以获取完整详细信息。
更多生命周期省略规则
让我们谈谈一个在两个版本中都可用的功能:我们为 impl
块和函数定义添加了一些额外的省略规则。像这样的代码
impl<'a> Reader for BufReader<'a> {
// methods go here
}
现在可以这样写
impl Reader for BufReader<'_> {
// methods go here
}
'_
生命周期仍然表明 BufReader
接受一个参数,但我们不再需要为它创建名称了。
生命周期仍然需要在结构体中定义。但是,我们不再需要像以前那样多的样板代码
// Rust 2015
struct Ref<'a, T: 'a> {
field: &'a T
}
// Rust 2018
struct Ref<'a, T> {
field: &'a T
}
: 'a
是推断出来的。如果您愿意,您仍然可以明确指定。我们正在考虑将来在此处添加更多省略选项,但还没有具体计划。
const fn
在 Rust 中定义函数有几种方法:使用 fn
的常规函数,使用 unsafe fn
的不安全函数,使用 extern fn
的外部函数。此版本添加了一种新的限定函数的方式:const fn
。它看起来像这样
const fn foo(x: i32) -> i32 {
x + 1
}
const fn
可以像常规函数一样调用,但它也可以在任何常量上下文中使用。当它被使用时,它是在编译时进行评估的,而不是在运行时进行评估。例如
const SIX: i32 = foo(5);
这将在编译时执行 foo
,并将 SIX
设置为 6
。
const fn
不能做所有 fn
可以做的事情;它们必须具有确定性的输出。这对健壮性至关重要。目前,const fn
可以执行最小的操作子集。以下是一些您可以执行的操作示例
- 整数上的算术和比较运算符
- 除
&&
和||
之外的所有布尔运算符 - 构造数组、结构体、枚举和元组
- 调用其他
const fn
- 数组和切片上的索引表达式
- 结构体和元组上的字段访问
- 从常量读取(但不是静态,甚至不获取对静态的引用)
- 引用的
&
和*
- 强制转换,除了原始指针到整数的强制转换
我们将扩展 const fn
的功能,但我们已经决定,这些功能足以开始发布该功能本身。
有关完整详细信息,请参阅参考。
新工具
2018 版本标志着 Rust 工具生态系统的成熟度达到了一个新水平。Cargo、Rustdoc 和 Rustup 自 1.0 以来一直是至关重要的工具;在 2018 版本中,新一代工具已准备好供所有用户使用:Clippy、Rustfmt 和 IDE 支持。
Rust 的 linter,clippy
,现在可以在稳定的 Rust 上使用。您可以通过 rustup component add clippy
安装它,并使用 cargo clippy
运行它。Clippy 现在被认为是 1.0,它具有与 rustc 相同的 lint 稳定性保证。可能会添加新的 lint,并且可能会修改 lint 以添加更多功能,但是 lint 永远不会被删除(只会被弃用)。这意味着在 clippy 下编译的代码将继续在 clippy 下编译(前提是没有通过 deny
设置为错误的 lint),但可能会发出新的警告。
Rustfmt 是一个用于格式化 Rust 代码的工具。通过使用官方 Rust 样式自动格式化代码,您可以节省时间和争论。您可以使用 rustup component add rustfmt
安装它,并使用 cargo fmt
使用它。
此版本包含 Rustfmt 1.0。从现在开始,我们保证 Rustfmt 的向后兼容性:如果您今天可以格式化代码,那么格式在将来不会改变(只有使用默认选项)。向后兼容性意味着在您的 CI 上运行 Rustfmt 是可行的(使用 cargo fmt -- --check
)。尝试使用它以及编辑器中的“保存时格式化”来彻底改变您的工作流程。
IDE 支持是 Rust 最需要的工具功能之一。现在有多种高质量的选项
IDE 支持的工作尚未完成,特别是基于 RLS 的编辑器中的代码补全还没有达到标准。但是,如果您主要想要对类型、文档和“转到定义”等的支持,那么您应该会很满意。
如果您在使用 Rustup 安装任何工具时遇到问题,请尝试运行 rustup self update
,然后重试。
工具 Lint
在Rust 1.30 中,我们稳定了“工具属性”,例如 #[rustfmt::skip]
。在 Rust 1.31 中,我们稳定了类似的东西:“工具 Lint”,例如 #[allow(clippy::bool_comparison)]
这些为 Lint 提供了一个命名空间,以便更清楚地了解它们来自哪个工具。
如果您以前使用过 Clippy 的 Lint,您可以像这样迁移
// old
#![cfg_attr(feature = "cargo-clippy", allow(bool_comparison))]
// new
#![allow(clippy::bool_comparison)]
您不再需要 cfg_attr
了!您还会收到警告,帮助您更新到新样式。
文档
Rustdoc 今年进行了许多改进,我们还发布了“Rust 编程语言”的完整重写版本。此外,您还可以 从 No Starch Press 购买纸质版!
我们之前称这本书为“第二版”,但由于它是第一版印刷版,所以很令人困惑。我们还想定期更新印刷版。最后,经过与 No Starch 的多次讨论,我们将在每次发布时更新网站上的书籍,No Starch 将定期提取我们的更改并进行印刷。这本书到目前为止销量很好,为 Black Girls Code 筹集了资金。
您可以在 这里 找到新的 TRPL。
领域工作组
今年我们宣布成立四个工作组
- 网络服务
- 命令行应用程序
- WebAssembly
- 嵌入式设备
每个小组都在努力为每个领域使 Rust 变得更棒。一些亮点
- 网络服务一直在完善 Futures 接口,以及在其之上的 async/await。这还没有发布,但我们快了!
- CLI 工作组一直在致力于库和文档,以创建很棒的命令行应用程序
- WebAssembly 小组一直在发布大量世界一流的工具,用于将 Rust 与 wasm 一起使用。
- 嵌入式设备已在稳定版 Rust 上实现了 ARM 开发!
您可以在新网站上了解更多关于这项工作的信息!
新网站
上周 我们宣布了网站的新版本。现在它已经升级到 rust-lang.org 本身了!
还有很多工作要做,但我们为许多人花费一年的时间来发布它而感到自豪。
库稳定化
添加了许多 From
实现
u8
现在实现了From<NonZeroU8>
,其他数字类型及其NonZero
等效类型也是如此Option<&T>
实现了From<&Option<T>>
,&mut
也是如此
此外,这些函数已稳定
slice::align_to
及其可变对应项slice::chunks_exact
,以及其可变和r
对应项(如slice::rchunks_exact_mut
)的所有组合
有关更多信息,请参阅 详细的发布说明。
Cargo 功能
Cargo 现在将使用 HTTP/2 并行下载包。
此外,现在 extern crate
通常不再需要,因此使用 extern crate foo as baz;
来重命名 crate 会很突兀。因此,您可以在 Cargo.toml
中执行此操作,如下所示
[dependencies]
baz = { version = "0.1", package = "foo" }
或者,等效的
[dependencies.baz]
version = "0.1"
package = "foo"
现在,foo
包可以通过您的代码中的 baz
来使用。
有关更多信息,请参阅 详细的发布说明。
1.31.0 的贡献者
在发布文章的结尾,我们通常会感谢 为本次发布做出贡献的人。但对于本次发布,与其他发布相比,此列表并未真正体现出贡献者的工作量和人数。每次发布只有六周,但本次发布是三年努力的成果,涉及无数个仓库,由无数人贡献。与大家一起工作真是太愉快了,我们期待在接下来的三年里继续成长。