Rust 团队很高兴地宣布 Rust 新版本 1.66.0 的发布。Rust 是一种编程语言,旨在赋予每个人构建可靠且高效软件的能力。
如果您之前通过 rustup 安装了 Rust,您可以使用以下命令获取 1.66.0 版本:
$ rustup update stable
如果您还没有安装,您可以从我们网站的相应页面获取 rustup
,并查看 GitHub 上 1.66.0 版本的详细发布说明。
如果您想通过测试未来版本来帮助我们,您可以考虑在本地更新以使用 beta 频道 (rustup default beta
) 或 nightly 频道 (rustup default nightly
)。请报告您可能遇到的任何错误!
1.66.0 稳定版的新特性
带字段枚举的显式判别值
具有整数表示的枚举现在可以使用显式判别值,即使它们带有字段。
#[repr(u8)]
enum Foo {
A(u8),
B(i8),
C(bool) = 42,
}
以前,您可以在具有表示的枚举上使用显式判别值,但前提是它们的变体都没有字段。当跨语言边界传递值时,显式判别值非常有用,在这些情况下,枚举的表示形式需要在两种语言中匹配。例如:
#[repr(u8)]
enum Bar {
A,
B,
C = 42,
D,
}
这里,Bar
枚举保证具有与 u8
相同的布局。此外,Bar::C
变体保证具有 42 的判别值。未明确指定值的变体将根据它们在源代码中的顺序自动分配判别值,因此 Bar::A
将具有 0 的判别值,Bar::B
将具有 1 的判别值,而 Bar::D
将具有 43 的判别值。如果没有此功能,设置 Bar::C
的显式值的唯一方法是在它之前添加 41 个不必要的变体!
注意:虽然对于无字段枚举,可以通过 as
转换(例如 Bar::C as u8
)来检查判别值,但 Rust 没有提供任何语言级别的方法来访问带有字段的枚举的原始判别值。相反,目前必须使用不安全代码来检查带有字段的枚举的判别值。由于此功能旨在用于跨语言 FFI,而 FFI 本身已经需要不安全代码,因此这应该不会带来太大的额外负担。同时,如果您只需要判别值的不透明句柄,请参阅 std::mem::discriminant
函数。
core::hint::black_box
在进行基准测试或检查编译器生成的机器代码时,通常需要阻止在某些地方发生优化。在以下示例中,函数 push_cap
在循环中执行 Vec::push
4 次:
fn push_cap(v: &mut Vec<i32>) {
for i in 0..4 {
v.push(i);
}
}
pub fn bench_push() -> Duration {
let mut v = Vec::with_capacity(4);
let now = Instant::now();
push_cap(&mut v);
now.elapsed()
}
如果您检查 x86_64 上编译器优化后的输出,您会注意到它看起来相当短:
example::bench_push:
sub rsp, 24
call qword ptr [rip + std::time::Instant::now@GOTPCREL]
lea rdi, [rsp + 8]
mov qword ptr [rsp + 8], rax
mov dword ptr [rsp + 16], edx
call qword ptr [rip + std::time::Instant::elapsed@GOTPCREL]
add rsp, 24
ret
实际上,我们想要进行基准测试的整个函数 push_cap
都被优化掉了!
我们可以使用新稳定的 black_box
函数来解决这个问题。从功能上讲,black_box
并没有什么特别之处:它接受您传递给它的值,然后将其原样返回。然而,在内部,编译器将 black_box
视为一个可以对其输入执行任何操作并返回任何值的函数(顾名思义)。
这对于禁用像我们上面看到的那种优化非常有用。例如,我们可以向编译器暗示,在 for 循环的每次迭代之后,实际上都会使用该向量来做一些事情。
use std::hint::black_box;
fn push_cap(v: &mut Vec<i32>) {
for i in 0..4 {
v.push(i);
black_box(v.as_ptr());
}
}
现在我们可以在我们的优化后的汇编输出中找到展开的 for 循环:
mov dword ptr [rbx], 0
mov qword ptr [rsp + 8], rbx
mov dword ptr [rbx + 4], 1
mov qword ptr [rsp + 8], rbx
mov dword ptr [rbx + 8], 2
mov qword ptr [rsp + 8], rbx
mov dword ptr [rbx + 12], 3
mov qword ptr [rsp + 8], rbx
您还可以在此汇编输出中看到调用 black_box
的副作用。指令 mov qword ptr [rsp + 8], rbx
在每次迭代后都会无用地重复。此指令将地址 v.as_ptr()
写入为函数的第一个参数,但该函数实际上从未被调用。
请注意,生成的代码根本不关心 push
调用可能引入的分配。这是因为编译器仍然使用我们在 bench_push
函数中调用 Vec::with_capacity(4)
的事实。您可以尝试调整 black_box
的位置,或尝试在多个位置使用它,以查看其对编译器优化的影响。
cargo remove
在 Rust 1.62.0 中,我们引入了 cargo add
,这是一个用于向您的项目添加依赖项的命令行实用程序。现在您可以使用 cargo remove
来删除依赖项。
稳定的 API
proc_macro::Span::source_text
u*::{checked_add_signed, overflowing_add_signed, saturating_add_signed, wrapping_add_signed}
i*::{checked_add_unsigned, overflowing_add_unsigned, saturating_add_unsigned, wrapping_add_unsigned}
i*::{checked_sub_unsigned, overflowing_sub_unsigned, saturating_sub_unsigned, wrapping_sub_unsigned}
BTreeSet::{first, last, pop_first, pop_last}
BTreeMap::{first_key_value, last_key_value, first_entry, last_entry, pop_first, pop_last}
- 为 WASI 上的 stdio 锁类型添加
AsFd
实现。 impl TryFrom<Vec<T>> for Box<[T; N]>
core::hint::black_box
Duration::try_from_secs_{f32,f64}
Option::unzip
std::os::fd
其他更改
Rust 1.66 版本中还有其他更改,包括:
- 现在你可以在模式中使用
..=X
范围。 - Linux 构建现在分别使用 LTO 和 BOLT 优化 rustc 前端和 LLVM 后端,从而提高运行时性能和内存使用率。
查看 Rust、Cargo 和 Clippy 中的所有更改。
1.66.0 版本的贡献者
许多人共同努力创建了 Rust 1.66.0。没有你们所有人,我们不可能做到。谢谢!