模式匹配
这篇讲 Rust 最强大的控制流工具——模式匹配。match 表达式配上 Rust 的枚举,能写出比 Go 的 switch、Python 的 if-elif 更安全、更表达力的代码。编译器甚至会检查你是否覆盖了所有情况。
match 表达式
match 逐一将值与每个模式比较,执行第一个匹配到的分支:
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => {
println!("Lucky penny!");
1
}
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}match 是穷尽的(exhaustive)——编译器强制你必须覆盖所有可能的取值。如果忘了一个分支,代码无法编译:
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
// ❌ 缺少 Quarter 分支——编译错误!
}
}这是 Rust 给程序员的免费安全网——每次新增枚举变体,编译器会帮你找到所有需要更新的 match 表达式。
匹配带值的变体
模式可以「解构」变体中携带的数据:
#[derive(Debug)]
enum UsState {
Alabama, Alaska, // ...
}
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
}
}
}匹配 Option<T>
处理 Option<T> 是 match 最常见的用途——这是在 Rust 中安全解包值的标准方式:
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); // Some(6)
let none = plus_one(None); // None
}通配模式和 _ 占位符
fn main() {
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(), // _ 不绑定值,表示「其余全部」
}
}
fn add_fancy_hat() {}
fn remove_fancy_hat() {}
fn move_player(_spaces: u8) {}
fn reroll() {}other 绑定匹配到的值(可以在代码块中使用),_ 不绑定值(告诉编译器「我不关心这个值」)。当不需要用到匹配值时,用 _ 更清晰。if let
当只关心一种匹配情况而忽略其余时,if let 比 match 更简洁:
fn main() {
let config_max = Some(3u8);
// match 写法(啰嗦)
match config_max {
Some(max) => println!("The maximum is configured to be {max}"),
_ => (),
}
// if let 写法(简洁)
if let Some(max) = config_max {
println!("The maximum is configured to be {max}");
}
// 带 else 的 if let
if let Some(max) = config_max {
println!("max = {max}");
} else {
println!("没有配置最大值");
}
}匹配进阶技巧
匹配多个模式
let x = 1;
match x {
1 | 2 => println!("one or two"), // | 分隔多个模式
3 => println!("three"),
_ => println!("anything"),
}匹配范围
let x = 5;
match x {
1..=5 => println!("one through five"), // 1 到 5(含 5)
_ => println!("something else"),
}守卫条件(match guard)
let num = Some(4);
match num {
Some(x) if x < 5 => println!("小于 5: {x}"), // 守卫条件
Some(x) => println!("{x}"),
None => (),
}@ 绑定
let msg = Message::Hello { id: 5 };
match msg {
Message::Hello { id: id_var @ 3..=7 } => {
println!("id 在 3..=7 范围内: {id_var}");
}
Message::Hello { id: 10..=12 } => {
println!("id 在 10..=12 范围内")
}
Message::Hello { id } => {
println!("id 是其它值: {id}")
}
}
enum Message {
Hello { id: i32 },
}一句话小结
match 是穷尽式的模式匹配——编译器强制覆盖所有情况,配合 Option<T> 和 Result<T, E> 使用安全高效。if let 处理「只关心一种」的场景,守卫条件和 @ 绑定处理复杂条件。Go 程序员:这就是你在 switch 里梦想的功能。下一篇讲 Trait 与泛型。
练习
用
match实现函数describe_number(n: i32) -> &str:- 负数 → “负数”
- 0 → “零”
- 正数(<=100)→ “小正数”
- 正数(>100)→ “大正数”
有一个
Option<String>变量name,用if let打印「Hello, {name}!」,如果是None则打印「Hello, guest!」。
参考答案
fn describe_number(n: i32) -> &'static str {
match n {
..=-1 => "负数",
0 => "零",
1..=100 => "小正数",
_ => "大正数",
}
}
fn main() {
let name = Some(String::from("Rust"));
// 方法 1:if let + else
if let Some(n) = &name {
println!("Hello, {n}!");
} else {
println!("Hello, guest!");
}
// 方法 2:match
match &name {
Some(n) => println!("Hello, {n}!"),
None => println!("Hello, guest!"),
}
}最后更新于