Rust 团队很高兴地宣布 Rust 的新版本 1.31.0 以及“Rust 2018”。Rust 是一种编程语言,它使每个人都能构建可靠且高效的软件。
如果您之前通过 rustup 安装了 Rust 版本,那么获取 Rust 1.31.0 非常简单,只需执行以下操作:
$ rustup update stable
如果您还没有安装 rustup,您可以从我们网站上的相应页面获取 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 今年进行了一些改进,而且我们还发布了完全重写的《The Rust Programming Language》。此外,你还可以从 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 的贡献者
在发布文章的结尾,我们通常会感谢为本次发布做出贡献的人。但是,对于此版本而言,与其他版本相比,此列表并不能真正反映出工作量和贡献人数。每个发布版本只有六周的时间,但是此版本是无数人三年努力的结晶,在无数个存储库中完成。很高兴与大家合作,我们期待在未来三年继续成长。