mirror of
https://github.com/DefectingCat/DefectingCat.github.io
synced 2025-07-15 16:51:37 +00:00
143 lines
3.6 KiB
Plaintext
143 lines
3.6 KiB
Plaintext
---
|
||
title: Rust 模式匹配
|
||
date: '2023-02-07'
|
||
tags: [Rust]
|
||
---
|
||
|
||
## match
|
||
|
||
`match` 是一个极为强大的控制流运算符,它允许我们将一个值与一系列的模式相比较,并根据相匹配的模式执行相应代码。模式可由字面值、变量、通配符和许多其他内容构成。
|
||
|
||
```rust
|
||
fn value_in_cents(coin: Coin) -> u8 {
|
||
match coin {
|
||
Coin::Penny => {
|
||
println!("Lucky penny!");
|
||
1
|
||
}
|
||
Coin::Nickel => 5,
|
||
Coin::Dime => 10,
|
||
Coin::Quarter => 25,
|
||
}
|
||
}
|
||
```
|
||
|
||
### 绑定值的模式
|
||
|
||
在匹配到绑定值的模式后,可以增加一个变量用来表示所匹配到的值。
|
||
|
||
```rust
|
||
#[derive(Debug)] // 这样可以立刻看到州的名称
|
||
enum UsState {
|
||
Alabama,
|
||
Alaska,
|
||
// --snip--
|
||
}
|
||
|
||
enum Coin {
|
||
Penny,
|
||
Nickel,
|
||
Dime,
|
||
Quarter(UsState),
|
||
}
|
||
|
||
fn value_in_cents(coin: Coin) -> u8 {
|
||
match coin {
|
||
Coin::Penny => 1,
|
||
Coin::Nickel => 5,
|
||
Coin::Dime => 10,
|
||
Coin::Quarter(state) => {
|
||
println!("State quarter from {:?}!", state);
|
||
25
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
这里的 `Coin::Quarter(state)` 就是匹配到的对应的 `UsState` 的值。
|
||
|
||
### 匹配 Option
|
||
|
||
```rust
|
||
fn plus_one(target: Option<i32>) -> Option<i32> {
|
||
match target {
|
||
Some(x) => Some(x + 1),
|
||
None => None,
|
||
}
|
||
}
|
||
|
||
fn main() {
|
||
let my_num = Some(41);
|
||
let my_num = plus_one(my_num);
|
||
println!("My number: {:?}", my_num);
|
||
}
|
||
```
|
||
|
||
这里 Clippy 可能会提示我们有更好的写法:
|
||
|
||
```rust
|
||
fn plus_one(target: Option<i32>) -> Option<i32> {
|
||
target.map(|x| x + 1);
|
||
target
|
||
}
|
||
|
||
fn main() {
|
||
let my_num = Some(41);
|
||
let my_num = plus_one(my_num);
|
||
println!("My number: {:?}", my_num);
|
||
}
|
||
```
|
||
|
||
`Option<T>` 上有个 `map()` 方法,可以更好的代替 `match Option` 来做匹配。
|
||
|
||
### 穷尽的匹配
|
||
|
||
`match` 的匹配是穷尽的,必须穷举到最后的可能性来使代码有效。
|
||
|
||
但可以使用一个特定值来采取默认操作,有点类似于 `switch` 语句中的 `default` 。
|
||
|
||
```rust
|
||
let dice_roll = 9;
|
||
match dice_roll {
|
||
3 => add_fancy_hat(),
|
||
7 => remove_fancy_hat(),
|
||
_ => (),
|
||
}
|
||
```
|
||
|
||
这个例子也满足穷举性要求,因为我们在最后一个分支中明确地忽略了其他的值。我们没有忘记处理任何东西。
|
||
|
||
## if let
|
||
|
||
为了满足 `match` 的穷尽性的要求,必须穷举到所有的可能性。尽管可以使用默认操作,但是最少也需要两行语句。
|
||
|
||
```rust
|
||
fn main() {
|
||
let config_max = Some(3u8);
|
||
match config_max {
|
||
Some(config) => println!("The maximum is configured to be {}", config),
|
||
_ => (),
|
||
}
|
||
}
|
||
```
|
||
|
||
不过我们可以使用 `if let` 这种更短的方式编写。
|
||
|
||
```rust
|
||
fn main() {
|
||
let config_max = Some(3u8);
|
||
|
||
if let Some(config) = config_max {
|
||
println!("The maximum is configured to be {}", config);
|
||
}
|
||
}
|
||
```
|
||
|
||
如果使用了 Clippy 这里也会提示我们可以使用更短的写法。
|
||
|
||
`if let` 语法获取通过等号分割的一个模式和一个表达式。它的工作方式与 `match` 相同。这里等号右边的表达式对应着 `match` ,而左边的模式对应着 `match` 的第一个分支。
|
||
|
||
在这个例子中,模式是 `Some(max)`,`max` 绑定为 `Some` 中的值。接着可以在 `if let` 代码块中使用 `max` 了,就跟在对应的 `match` 分支中一样。模式不匹配时 `if let` 块中的代码不会执行。
|
||
|
||
换句话说,可以认为 `if let` 是 `match` 的一个语法糖,它当值匹配某一模式时执行代码而忽略所有其他值。
|