---
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` 的一个语法糖，它当值匹配某一模式时执行代码而忽略所有其他值。
