Rust 新版解读 | 1.66 | 重点: 有字段枚举的显示判别
Rust 1.66 官方 release doc: Announcing Rust 1.66.0 | Rust Blog
通过 rustup 安装的同学可以使用以下命令升级到 1.66 版本:
$ rustup update stable
对有字段枚举的显示判别
枚举的显示判别在跨语言传递值时很关键,需要两个语言里每个枚举值的判别是一致的,比如:
#![allow(unused)] fn main() { #[repr(u8)] enum Bar { A, B, C = 42, D, } }
这个例子里,枚举 Bar
使用了 u8
作为原语表形(representation),并且 Bar::C
使用 42 来判别,其它没有显示判别的枚举值会按照源码里地顺序自动地递增赋值,这里的 Bar::A
是0,Bar::B
是1,Bar::D
是43。如果没有显示判别,那就只能在 Bar::B
和 Bar::C
之间加上 40 个无意义的枚举值了。
在1.66之前,枚举的显示判别只能用在无字段枚举上。现在对有字段枚举的显示判别也稳定了:
#![allow(unused)] fn main() { #[repr(u8)] enum Foo { A(u8), B(i8), C(bool) = 42, } }
注意:可以通过 as
转换(比如 Bar::C as u8
)来判断一个无字段枚举的判别值,但是 Rust 还没有给有字段枚举提供语言层面上的获取原始判别值的方法,只能通过 unsafe 的代码来检查有字段枚举的判别值。考虑到这个使用场景往往出现在必须使用 unsafe 代码的跨语言的 FFI 里,希望这没有造成太大的负担。如果你的确需要的话,参考 std::mem::discriminant
。
黑盒方法 core::hint::black_box
当对编译器产生的代码做基准测试时,常常需要阻止一些优化,比如下面的代码里, push_cap
在一个循环里执行了4次 Vec::push
:
#![allow(unused)] fn main() { 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 机器上编译的优化输出结果,你会注意到整个 push_cap
方法都被优化掉了...
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
现在可以通过调用 black_box
来避免类似情况的发送。 虽然实际上 black_box
内部只会取走值并直接返回,但是编译器会认为这个方法可能做任何事情。
#![allow(unused)] fn main() { 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()); } } }
这样就可以得到展开循环的结果:
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
指令都不用考虑内存分配的问题,这是因为编译器运行在 Vec::with_capacity(4)
的条件下。你可以尝试改动一下 black_box
的位置或者在多处使用,来看看其对编译的优化输出的影响。
cargo remove
1.62里我们引入了 cargo add
来通过命令行给你的项目增加依赖项。现在可以使用 cargo remove
来移除依赖了。
Others
其它更新细节,和稳定的API列表,参考原Blog