深入浅出 Rust 的强大 match 表达式

一、什么是 match

match 表达式能让你将一个值与一系列"模式"进行比较,并根据匹配到的模式执行相应的代码。常见的模式类型包括:

  • 字面量(例如 37"hello"
  • 枚举(Enum)变体(例如 Coin::Penny
  • 变量绑定(如在 Coin::Quarter(state) 中捕获 state
  • 通配符(_),用来匹配任何值
  • 其他更高级的模式(在 Rust 程序设计语言 第 19 章会有深入讲解)

从结构上看,可将 match 类比为一个"分类器"或"检索器":

  1. 传入的值依次与每个模式进行匹配。
  2. 一旦找到匹配的模式,运行对应的代码,并返回结果。
  3. 如果没有匹配到,继续检查下一个模式。
  4. match 必须覆盖所有可能的值,也就是"穷尽匹配"。

二、示例:硬币分类

先来一个经典示例。假设我们有一个表示美国硬币的枚举 Coin

rust 复制代码
#[derive(Debug)]
enum UsState {
    Alaska,
    // ... 其他州
}

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter(UsState),
}

如果要写一个函数 value_in_cents,返回某个硬币的美分值,就可以这样:

rust 复制代码
fn value_in_cents(coin: Coin) -> u8 {
    match coin {
        Coin::Penny => 1,
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter(state) => {
            println!("来自 {:?} 州的 25 美分硬币!", state);
            25
        }
    }
}

2.1 解析

  1. match coin :这里的 coin 就是要进行"模式匹配"的值。
  2. 匹配分支(arms) :每一行(如 Coin::Penny => 1)被称为一个"匹配分支"或"分支模式"。
  3. 模式与代码Coin::Penny => 1 表示如果 coin 的值是 Coin::Penny,就返回 1。对于 Coin::Quarter(state),还会打印出所在州,并返回 25

Rust 会逐个检查 coin 与各个分支是否匹配,一旦匹配到就执行对应的代码并返回该结果。因为我们覆盖了 PennyNickelDimeQuarter 四种变体,所以 Rust 确定所有情况都被"穷尽",编译通过。

三、绑定内部值

Coin::Quarter(state) 这个分支里,我们不仅仅匹配到 Quarter,还把这个硬币的具体"州"数据绑定到了变量 state 上,然后就能在分支中使用它,比如打印或做进一步处理。这种模式绑定(pattern binding)让我们可以优雅地解构枚举内部的数据。

四、与 Option<T> 搭配使用

在实际开发中,match 常被用来搭配 Option<T> 处理"有值"或"无值"的情形。示例:我们要写一个函数 plus_one,它接受 Option<i32>,如果里面有值就加一,如果没有值就原样返回 None

rust 复制代码
fn plus_one(x: Option<i32>) -> Option<i32> {
    match x {
        None => None,
        Some(i) => Some(i + 1),
    }
}

4.1 工作原理

  • 当传入 None 时,与分支 None => None 匹配,直接返回 None
  • 当传入 Some(i) 时,与分支 Some(i) => Some(i + 1) 匹配,此时 i 会绑定到内部的数字,然后执行 i + 1,最后返回新的 Some(i + 1)

借助枚举的严格穷尽性,Rust 强制要求我们显式处理 None,从而避免了"null 引用"带来的潜在错误。

五、穷尽匹配:覆盖所有可能分支

Rust 对 match 的"穷尽匹配"有严格要求:你必须确保所有可能情况都能被匹配到,否则编译器会报错提示遗漏了哪些情况。比如,我们如果在 plus_one 中遗漏了 None

rust 复制代码
fn plus_one_bug(x: Option<i32>) -> Option<i32> {
    match x {
        Some(i) => Some(i + 1),
    }
}

编译器就会报错,提醒你还没有处理 None。这项特性让我们在编译期就能暴露逻辑漏洞,大幅降低 bug 产生的概率。

六、通配模式 _

在有些场景下,我们只想对少数情况做特殊处理,所有其他情况统一处理。Rust 提供了 _ 来做"通配"匹配(也称"捕获所有剩余情况")。例如:

rust 复制代码
fn dice_roll_outcome(roll: u8) {
    match roll {
        3 => fancy_hat(),
        7 => remove_hat(),
        _ => reroll(), // 其余的全部执行 reroll()
    }
}

fn fancy_hat() { /* ... */ }
fn remove_hat() { /* ... */ }
fn reroll() { /* ... */ }

这里 _ 可以匹配任何值 ,但不会将其绑定到变量中。如果你确实需要使用那个值,可以把 _ 换成一个命名变量(例如 other),这样就能在分支里使用了。

七、分支中的多行代码

大多数时候,每个匹配分支只返回一个简单值,不需要花括号。如果需要在分支中执行多行操作,可以使用花括号包起来,最后一行将作为返回值。例如:

rust 复制代码
fn value_in_cents(coin: Coin) -> u8 {
    match coin {
        Coin::Penny => {
            println!("幸运的便士!");
            1
        },
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter(state) => {
            println!("来自 {:?} 州的 25 美分硬币!", state);
            25
        },
    }
}

这里当匹配到 Coin::Penny 时,会先打印 "幸运的便士!",然后返回 1

八、总结与收获

  1. 强大的模式匹配

    Rust 的模式匹配远不止简单的"值相等"。后续还可以学习更高级的模式,如解构结构体、元组、范围匹配、匹配守卫(guard)等。

  2. 穷尽性
    match 要求你必须处理所有可能情况,这在很大程度上减少了遗漏错误。

  3. Enum + match = 强力组合

    Enum 天生与 match 搭配;在模式中既能指定变体,又能访问内部数据。

  4. 通配模式 _
    _ 用于捕获其他未匹配的情况,避免写过多重复分支;若要使用未匹配的值,则使用命名变量替代 _

  5. 匹配分支的返回值

    匹配分支中的代码是一个表达式,match 的最终结果就是匹配分支所产生的值。

九、结语

Rust 的 match 表达式是编写安全、易读且可靠的代码的有力工具。它不仅迫使你在编译阶段考虑到所有可能的情况,而且还能让你专注于"要对每种情况如何处理"这一核心逻辑。很多 Rust 开发者在接触到这种枚举加模式匹配的风格后,纷纷表示"再也离不开了"。

如果你想进一步了解更多高级模式的细节(比如范围匹配、复杂数据结构的解构匹配等),可以阅读 The Rust Programming Language 第 19 章。相信随着对 match 越来越熟悉,你会发现它能让 Rust 代码既优雅又高效!

祝你匹配愉快,写出更多健壮且清晰的 Rust 代码!

相关推荐
蚂蚁背大象19 分钟前
Rust 所有权系统是为了解决什么问题
后端·rust
布列瑟农的星空1 小时前
前端都能看懂的rust入门教程(五)—— 所有权
rust
子玖2 小时前
go实现通过ip解析城市
后端·go
Java不加班2 小时前
Java 后端定时任务实现方案与工程化指南
后端
心在飞扬2 小时前
RAG 进阶检索学习笔记
后端
Moment2 小时前
想要长期陪伴你的助理?先从部署一个 OpenClaw 开始 😍😍😍
前端·后端·github
Das1_2 小时前
【Golang 数据结构】Slice 底层机制
后端·go
得物技术2 小时前
深入剖析Spark UI界面:参数与界面详解|得物技术
大数据·后端·spark
古时的风筝2 小时前
花10 分钟时间,把终端改造成“生产力武器”:Ghostty + Yazi + Lazygit 配置全流程
前端·后端·程序员