Rust match 控制流结构
深入掌握 Rust match 表达式:穷尽性模式匹配、绑定枚举数据、Option 优雅处理及通配分支。
Rust 提供了极其强大的控制流运算符 —— match。它允许你将一个值与一系列模式进行比对,并根据首先匹配的模式执行相应的代码分支。
浅显易懂(ELI5):硬币分拣机#
把 match 想象成一台硬币分拣机:
- 你将一枚硬币投入顶部的投币口。
- 硬币沿着带有不同大小孔径的滑道向下滚动。
- 一旦遇到第一个尺寸完全吻合的孔,它就会落入对应的收纳槽。
- 每一枚硬币只会在它落入的槽位触发对应的操作。
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) 组成。每个分支包含两部分:一个模式与对应的执行代码,中间用 => 运算符分隔。
src/main.rs
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => {
println!("幸运便士!");
1
}
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
fn main() {
let coin = Coin::Penny;
println!("价值: {} 美分", value_in_cents(coin));
}rust[!NOTE] 在 Rust 中
match是一个表达式(expression),它会返回一个值。同一个match中所有分支返回的值必须具有相同的类型。
绑定值的模式 (Patterns that Bind to Values)#
match 分支的一大特性是可以解构并绑定存储在枚举成员内部的数据:
src/main.rs
#[derive(Debug)]
enum UsState {
Alabama,
Alaska,
California,
}
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:?} 州的 25 美分硬币!");
25
}
}
}
fn main() {
let quarter = Coin::Quarter(UsState::Alaska);
println!("价值: {} 美分", value_in_cents(quarter));
}rust当传入 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使用通配变量与占位符 _#
当你只想处理特定几个分支,并把其他所有情况归入默认逻辑时:
src/main.rs
let dice_roll = 9;
match dice_roll {
3 => add_fancy_hat(),
7 => remove_fancy_hat(),
other => move_player(other), // 匹配其余所有值并绑定到变量 other
}
match dice_roll {
3 => add_fancy_hat(),
7 => remove_fancy_hat(),
_ => reroll(), // 匹配其余所有值,但不绑定变量
}
match dice_roll {
3 => add_fancy_hat(),
7 => remove_fancy_hat(),
_ => (), // 不做任何操作(返回单元值 unit tuple)
}
fn add_fancy_hat() {}
fn remove_fancy_hat() {}
fn move_player(_num: u8) {}
fn reroll() {}rust[!TIP] 通配模式(
other或_)必须放在match表达式的最末尾,因为分支是按从上到下的顺序依次匹配的。
总结#
match按顺序将值与各个模式进行匹配,并执行第一个命中的分支。- 分支可以直接解构并绑定枚举成员内部携带的数据。
- 编译器强制保证穷尽性,消除任何未处理分支引发的运行时漏洞。
- 使用
_或通配变量作为兜底分支快速处理其余情况。