|
|
|
@ -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<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` 方法都被优化掉了...
|
|
|
|
|
|
|
|
|
|
```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<i32>) {
|
|
|
|
|
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)
|
|
|
|
|
|