|
|
|
|
# Rust 新版解读 | 1.58 | 重点: 格式化字符串捕获环境中的值
|
|
|
|
|
|
|
|
|
|
众所周知,Rust 小版本发布非常频繁,6 周就发布一次,因此通常不会有特别值得普通用户关注的内容,但是这次 1.58 版本不同,新增了(stable 化了)一个非常好用的功能: **在格式化字符串时捕获环境中的值**。
|
|
|
|
|
|
|
|
|
|
> Rust 1.58 官方 release doc: [Announcing Rust 1.58.0 | Rust Blog](https://blog.rust-lang.org/2022/01/13/Rust-1.58.0.html)
|
|
|
|
|
|
|
|
|
|
## 在格式化字符串时捕获环境中的值
|
|
|
|
|
|
|
|
|
|
在以前,想要输出一个函数的返回值,你需要这么做:
|
|
|
|
|
```rust
|
|
|
|
|
fn get_person() -> String {
|
|
|
|
|
String::from("sunface")
|
|
|
|
|
}
|
|
|
|
|
fn main() {
|
|
|
|
|
let p = get_person();
|
|
|
|
|
println!("Hello, {}!", p); // implicit position
|
|
|
|
|
println!("Hello, {0}!", p); // explicit index
|
|
|
|
|
println!("Hello, {person}!", person = p);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
问题倒也不大,但是一旦格式化字符串长了后,就会非常冗余,而在 1.58 后,我们可以这么写:
|
|
|
|
|
```rust
|
|
|
|
|
fn get_person() -> String {
|
|
|
|
|
String::from("sunface")
|
|
|
|
|
}
|
|
|
|
|
fn main() {
|
|
|
|
|
let person = get_person();
|
|
|
|
|
println!("Hello, {person}!");
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
是不是清晰、简洁了很多?甚至还可以将环境中的值用于格式化参数:
|
|
|
|
|
```rust
|
|
|
|
|
let (width, precision) = get_format();
|
|
|
|
|
for (name, score) in get_scores() {
|
|
|
|
|
println!("{name}: {score:width$.precision$}");
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
但也有局限,它只能捕获普通的变量,对于更复杂的类型(例如表达式),可以先将它赋值给一个变量或使用以前的 `name = expression` 形式的格式化参数。
|
|
|
|
|
|
|
|
|
|
目前除了 `panic!` 外,其它接收格式化参数的宏,都可以使用新的特性。对于 `panic!` 而言,如果还在使用 `2015版本` 或 `2018版本` 版本 ,那 `panic!("{ident}")` 依然会被当成 正常的字符串来处理,同时编译器会给予 `warn` 提示。而对于 `2021版本` ,则可以正常使用:
|
|
|
|
|
```rust
|
|
|
|
|
fn get_person() -> String {
|
|
|
|
|
String::from("sunface")
|
|
|
|
|
}
|
|
|
|
|
fn main() {
|
|
|
|
|
let person = get_person();
|
|
|
|
|
panic!("Hello, {person}!");
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
输出:
|
|
|
|
|
```console
|
|
|
|
|
thread 'main' panicked at 'Hello, sunface!', src/main.rs:6:5
|
|
|
|
|
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 比 unwrap 更危险的 unwrap_unchecked
|
|
|
|
|
|
|
|
|
|
在 1.58 中为 `Option` 和 `Result` 新增了 `unwrap_unchecked` 方法,与 `unwrap` 遇到错误或者空值直接 `panic` 不同,`unwrap_unchecked` 遇到错误时处理方式糟糕的多:
|
|
|
|
|
```rust
|
|
|
|
|
fn get_num() -> Option<i32> {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
fn main() {
|
|
|
|
|
unsafe {
|
|
|
|
|
let n = get_num().unwrap_unchecked();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
输出如下:
|
|
|
|
|
```console
|
|
|
|
|
zsh: segmentation fault cargo run
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
嗯,段错误了,对比下 `panic`,有一种泪流满面的冲动:我要这不安全的方法何用?
|
|
|
|
|
|
|
|
|
|
其实,还真有些用:
|
|
|
|
|
|
|
|
|
|
- 想要较小的可执行文件时(嵌入式,WASM等),该方法就可以大显身手。因为 `panic` 会导致[二进制可执行文件变大不少](https://zhuanlan.zhihu.com/p/445465092)
|
|
|
|
|
- 它还可以提高一点性能, 因为编译器可能无法优化掉 `unwrap` 的指令分支, 虽然它只会增加区区几条分支预测指令
|