blog.dopana

Back

Rust 提供了极其强大的控制流运算符 —— match。它允许你将一个值与一系列模式进行比对,并根据首先匹配的模式执行相应的代码分支。

浅显易懂(ELI5):硬币分拣机#

match 想象成一台硬币分拣机:

  1. 你将一枚硬币投入顶部的投币口。
  2. 硬币沿着带有不同大小孔径的滑道向下滚动。
  3. 一旦遇到第一个尺寸完全吻合的孔,它就会落入对应的收纳槽。
  4. 每一枚硬币只会在它落入的槽位触发对应的操作。
graph TD
    Input["输入值 (Input Value)"] --> Arm1{"是否匹配模式 1?"}
    Arm1 -- 是 --> Action1["执行分支 1 并返回结果"]
    Arm1 -- 否 --> Arm2{"是否匹配模式 2?"}
    Arm2 -- 是 --> Action2["执行分支 2 并返回结果"]
    Arm2 -- 否 --> ArmCatch{"是否匹配通配模式 (_ / other)?"}
    ArmCatch -- 是 --> ActionCatch["执行默认分支"]

在 Rust 中,编译器提供更强大的安全性:它强制要求必须穷尽所有可能的情况(Exhaustiveness 检查),确保程序绝对不会因为未处理的分支而崩溃。

match 的基本语法#

match 表达式由若干 分支(arms) 组成。每个分支包含两部分:一个模式与对应的执行代码,中间用 => 运算符分隔。

[!NOTE] 在 Rust 中 match 是一个表达式(expression),它会返回一个值。同一个 match 中所有分支返回的值必须具有相同的类型。

绑定值的模式 (Patterns that Bind to Values)#

match 分支的一大特性是可以解构并绑定存储在枚举成员内部的数据:

当传入 Coin::Quarter(UsState::Alaska) 时,变量 state 会自动绑定到内部的 UsState::Alaska 值。

匹配 Option<T>#

Rust 没有 null,而是使用 Option<T>Some(T)None)。使用 match 是提取和安全操作 Option 内部值最标准的方式:

src/main.rs
fn plus_one(x: Option<i32>) -> Option<i32> {
    match x {
        None => None,
        Some(i) => Some(i + 1),
    }
}

fn main() {
    let five = Some(5);
    let six = plus_one(five);
    let none = plus_one(None);

    println!("five + 1 = {six:?}");
    println!("none + 1 = {none:?}");
}
rust

匹配必须是穷尽的 (Exhaustive) 与通配模式#

Rust 的匹配必须穷尽所有的可能性。如果有任何遗漏的分支,编译器会直接报错:

src/main.rs
// 编译错误:未覆盖 `None` 模式!
fn plus_one_invalid(x: Option<i32>) -> Option<i32> {
    match x {
        Some(i) => Some(i + 1),
    }
}
rust

使用通配变量与占位符 _#

当你只想处理特定几个分支,并把其他所有情况归入默认逻辑时:

[!TIP] 通配模式(other_)必须放在 match 表达式的最末尾,因为分支是按从上到下的顺序依次匹配的。

总结#

  • match 按顺序将值与各个模式进行匹配,并执行第一个命中的分支。
  • 分支可以直接解构并绑定枚举成员内部携带的数据。
  • 编译器强制保证穷尽性,消除任何未处理分支引发的运行时漏洞。
  • 使用 _ 或通配变量作为兜底分支快速处理其余情况。

参考资料#