diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 724b6be2..2dc5625a 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -345,3 +345,4 @@ - [1.63](appendix/rust-versions/1.63.md) - [1.64](appendix/rust-versions/1.64.md) - [1.65](appendix/rust-versions/1.65.md) + - [1.66](appendix/rust-versions/1.66.md) diff --git a/src/appendix/rust-versions/1.66.md b/src/appendix/rust-versions/1.66.md new file mode 100644 index 00000000..0340d035 --- /dev/null +++ b/src/appendix/rust-versions/1.66.md @@ -0,0 +1,112 @@ +# Rust 新版解读 | 1.66 | 重点: 有字段枚举的显示判别 + +> Rust 1.66 官方 release doc: [Announcing Rust 1.66.0 | Rust Blog](https://blog.rust-lang.org/2022/12/15/Rust-1.66.0.html) + +通过 [rustup](https://www.rust-lang.org/tools/install) 安装的同学可以使用以下命令升级到 1.66 版本: + +```shell +$ rustup update stable +``` + +## 对有字段枚举的显示判别 + +枚举的显示判别在跨语言传递值时很关键,需要两个语言里每个枚举值的判别是一致的,比如: + +```rust +#[repr(u8)] +enum Bar { + A, + B, + C = 42, + D, +} +``` + +这个例子里,枚举 `Bar` 使用了 `u8` 作为原语表型,并且 `Bar::C` 使用 42 来判别,其它没有显示判别的枚举值会按照源码里地顺序自动地递增赋值,这里的 `Bar::A` 是0,`Bar::B` 是1,`Bar::D` 是43。如果没有显示判别,那就只能在 `Bar::B` 和 `Bar::C` 之间加上 40 个无意义的枚举值了。 + +在1.66之前,枚举的显示判别只能用在无字段枚举上。现在对有字段枚举的显示判别也稳定了: + +```rust +#[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` : + +```rust +fn push_cap(v: &mut Vec) { + 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` 方法都被优化掉了... + +```text +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` 内部只会取走值并直接返回,但是编译器会认为这个方法可能做任何事情。 + +``` rust +use std::hint::black_box; + +fn push_cap(v: &mut Vec) { + for i in 0..4 { + v.push(i); + black_box(v.as_ptr()); + } +} +``` + +这样就可以得到展开循环的[结果](https://rust.godbolt.org/z/Ws1GGbY6Y): + +```text + 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](https://blog.rust-lang.org/2022/12/15/Rust-1.66.0.html#stabilized-apis) +