模式匹配
> 学习 match 表达式实现穷举模式匹配,以及 if let / while let 的简化用法。
if let
if let 语法
概念名称: if let 简化单一模式匹配。
语法结构:
┌──────────────────────────────────────┐
│ if let 模式 = 表达式 { │
│ ↑ ↑ │
│ 匹配模式 要匹配的值 │
│ │
│ 匹配成功时执行 │
│ } else { │
│ 匹配失败时执行 │
│ } │
│ │
│ if let Some(x) = opt { ... } │
│ if let Ok(v) = result { ... } │
└──────────────────────────────────────┘最简示例
rust
▶ Runfn main() {
let maybe = Some(5);
if let Some(x) = maybe {
println!("值:{}", x);
}
}详细示例
rust
▶ Runfn main() {
let config_max = Some(3u8);
// 使用 match(冗长)
match config_max {
Some(max) => println!("最大值:{}", max),
_ => println!("没有最大值"),
}
// 使用 if let(简洁)
if let Some(max) = config_max {
println!("最大值:{}", max);
}
// 带 else
let coin = Some(5u8);
if let Some(value) = coin {
println!("面值是:{}", value);
} else {
println!("这不是硬币");
}
}实际应用场景
rust
▶ Runfn main() {
// 场景 1:处理 Option
let name: Option<String> = Some("Alice".to_string());
if let Some(n) = name {
println!("名字是:{}", n);
}
// 场景 2:处理 Result
let result: Result<i32, &str> = Ok(42);
if let Ok(value) = result {
println!("成功:{}", value);
}
// 场景 3:带条件的 if let
let numbers = vec![1, 2, 3];
let first = numbers.first();
if let Some(&first_num) = first {
if first_num > 0 {
println!("第一个数是正数:{}", first_num);
}
}
}match 表达式
match 语法
概念名称: match 全面模式匹配,必须穷尽所有情况。
语法结构:
┌──────────────────────────────────────┐
│ match 值 { │
│ ↑ │
│ 要匹配的表达式 │
│ │
│ 模式1 => 结果1, │
│ 模式2 => 结果2, │
│ _ => 默认值, ← 通配符 │
│ } │
│ │
│ match x { │
│ 0 => "零", │
│ 1..=9 => "一位数", │
│ _ => "其他", │
│ } │
└──────────────────────────────────────┘最简示例
rust
▶ Runfn main() {
let x = 5;
let desc = match x {
0 => "零",
_ => "非零",
};
println!("{}", desc);
}详细示例
rust
▶ Runfn main() {
let number = 42;
match number {
0 => println!("零"),
1..=9 => println!("个位数"),
10..=99 => println!("十位数"),
100..=999 => println!("百位数"),
_ => println!("其他"), // _ 是通配符
}
}match 返回值
rust
▶ Runfn main() {
let number = 5;
let description = match number {
0 => "零",
n if n > 0 && n < 10 => "小的正数",
10..=99 => "两位数",
_ => "很大的数",
};
println!("{}", description);
}匹配 Option 和 Result
rust
▶ Runfn main() {
// 匹配 Option
let maybe_number: Option<i32> = Some(42);
match maybe_number {
Some(n) => println!("数字是:{}", n),
None => println!("没有数字"),
}
// 匹配 Result
let result: Result<i32, &str> = Ok(42);
match result {
Ok(value) => println!("成功:{}", value),
Err(error) => println!("错误:{}", error),
}
}迭代器方法
rust
▶ Runfn main() {
let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// all - 检查是否全部满足条件
let all_positive = numbers.iter().all(|&x| x > 0);
println!("全部是正数:{}", all_positive);
// any - 检查是否有任意满足条件
let has_even = numbers.iter().any(|&x| x % 2 == 0);
println!("有偶数:{}", has_even);
// find - 查找第一个满足条件的元素
let found = numbers.iter().find(|&&x| x > 5);
println!("第一个大于 5 的数:{:?}", found);
// position - 查找位置
let pos = numbers.iter().position(|&x| x == 5);
println!("5 的位置:{:?}", pos);
// take - 取前 N 个
let first_three: Vec<_> = numbers.iter().take(3).collect();
println!("前三个:{:?}", first_three);
// skip - 跳过前 N 个
let skip_three: Vec<_> = numbers.iter().skip(3).collect();
println!("跳过前三个:{:?}", skip_three);
// filter - 过滤
let evens: Vec<_> = numbers.iter().filter(|&&x| x % 2 == 0).collect();
println!("偶数:{:?}", evens);
// map - 转换
let doubled: Vec<_> = numbers.iter().map(|&x| x * 2).collect();
println!("翻倍:{:?}", doubled);
// fold - 累积(从左到右)
let sum: i32 = numbers.iter().fold(0, |acc, &x| acc + x);
println!("总和:{}", sum);
// rfund - 累积(从右到左)
let product: i32 = numbers.iter().fold(1, |acc, &x| acc * x);
println!("乘积:{}", product);
}语言对比:match vs switch
Rust match vs C/Java switch
┌─────────────────────────────────────────────────────┐
│ match vs switch 对比 │
├─────────────────────────────────────────────────────┤
│ │
│ C/Java switch: │
│ ├── switch (x) { │
│ │ case 0: printf("零"); break; │
│ │ case 1: printf("一"); break; │
│ │ default: printf("其他"); │
│ │ } │
│ ├── 问题: │
│ │ • 忘记 break 会穿透(常见 bug) │
│ │ • 不强制处理所有情况 │
│ │ • 只能匹配常量值 │
│ │ • 不能提取值 │
│ │
│ Rust match: │
│ ├── match x { │
│ │ 0 => println!("零"), │
│ │ 1 => println!("一"), │
│ │ _ => println!("其他"), │
│ │ } │
│ ├── 优势: │
│ │ • 无穿透问题(无需 break) │
│ │ • 必须穷尽所有情况(编译检查) │
│ │ • 可匹配模式(Some(x) 提取 x) │
│ │ • 可返回值 │
│ │
│ 对比结论: │
│ ├── switch:不安全,易出错 │
│ ├── match:安全,编译器保证 │
│ │
└─────────────────────────────────────────────────────┘实际代码对比
rust
▶ Run// Rust:match 返回值
fn describe(x: i32) -> &'static str {
match x {
0 => "零",
n if n > 0 => "正数",
_ => "负数",
}
}c
// C:switch 不能返回值
void describe(int x) {
switch (x) {
case 0:
printf("零");
break; // 忘记会穿透!
default:
printf("其他");
}
}java
// Java:switch 需要赋值
String describe(int x) {
String result;
switch (x) {
case 0:
result = "零";
break; // 必须写 break
default:
result = "其他";
}
return result;
}match 的独特优势
rust
▶ Run// Rust:提取值
let opt = Some(42);
match opt {
Some(x) => println!("值:{}", x), // 提取 x
None => println!("无值"),
}
// C/Java:需要额外代码提取值常见错误诊断
错误 1:match 未穷尽所有情况
错误代码
rust
▶ Runfn main() {
let opt: Option<i32> = Some(5);
match opt {
Some(x) => println!("{}", x),
// 缺少 None 处理
}
}编译器错误
error[E0004]: non-exhaustive patterns: `None` not covered
--> src/main.rs:4:11
|
4 | match opt {
| ^^^ pattern `None` not covered
|
= note: the matched value is of type `Option<i32>`
help: ensure that all possible cases are being handled by adding
a match arm with a wildcard pattern
|
4 | match opt {
| ^^^
5 | Some(x) => println!("{}", x),
6 + _ => todo!(),
|解读错误信息
┌─────────────────────────────────────────────────────┐
│ 错误信息解读 │
├─────────────────────────────────────────────────────┤
│ │
│ error[E0004]: non-exhaustive patterns │
│ │ │
│ │ 错误码 E0004 = "模式未穷尽" │
│ │ match 没有覆盖所有可能情况 │
│ │
│ `None` not covered │
│ │ │
│ │ 说明:缺少 None 情况处理 │
│ │ Option 有两个变体:Some 和 None │
│ │
│ help: ensure that all possible cases are being │
│ handled │
│ │ │
│ │ 建议:添加通配符 _ 或明确处理 None │
│ │ 解决方案: │
│ │ None => println!("无值"), │
│ │ 或 _ => todo!(), │
│ │
│ Rust 规则: │
│ • match 必须覆盖所有可能值 │
│ • 编译器保证无遗漏 │
│ │
│ 这是 Rust 安全的核心特性 │
│ │
└─────────────────────────────────────────────────────┘修复方案
rust
▶ Run// 方案 1:处理所有情况
fn main() {
let opt: Option<i32> = Some(5);
match opt {
Some(x) => println!("{}", x),
None => println!("无值"), // ✅ 添加 None
}
}
// 方案 2:使用通配符
fn main() {
let opt: Option<i32> = Some(5);
match opt {
Some(x) => println!("{}", x),
_ => println!("其他情况"), // ✅ 通配符
}
}错误 2:match 分支类型不一致
错误代码
rust
▶ Runfn main() {
let x = 5;
let result = match x {
0 => "零", // 返回 &str
1 => 42, // 返回 i32
_ => "其他",
};
}编译器错误
error[E0308]: mismatched types
--> src/main.rs:5:13
|
4 | let result = match x {
| ------- `&str` expected because of this
5 | 0 => "零",
| ^^^^ expected `&str`, found integer
|
= note: expected type `&str`
found type `{integer}`解读与修复
错误码 E0308:类型不匹配
原因:
├── match 返回值类型必须一致
├── 第一个分支:&str
├── 第二个分支:i32
├── 类型不一致
修复:
├── 方案 1:统一返回字符串
│ match x {
│ 0 => "零",
│ 1 => "一",
│ _ => "其他",
│ }
│
├── 方案 2:统一返回整数
│ match x {
│ 0 => 0,
│ 1 => 1,
│ _ => -1,
│ }错误 3:if let 方向错误
错误代码
rust
▶ Runfn main() {
let x = Some(5);
if let Some(5) = x { // ❌ 方向错误
println!("匹配");
}
}编译器错误
error: expected expression, found `let` statement
--> src/main.rs:3:8
|
3 | if let Some(5) = x {
| ^^^^^^^^^^^^^^^
|
= note: `if let` requires a pattern on the left, not a value解读与修复
错误:if let 方向错误
原因:
├── if let 模式 = 值(正确)
├── if let 值 = 模式(错误)
├── 模式在左边,值在右边
修复:
├── 正确写法:
│ if let Some(n) = x { // Some(n) 是模式
│ println!("{}", n);
│ }
│
├── 对比记忆:
│ if let Some(x) = opt ✅
│ if let opt = Some(x) ❌错误诊断速查
| 错误码 | 错误类型 | 原因 | 快速解决 |
|---|---|---|---|
| E0004 | match 未穷尽 | 缺少情况处理 | 添加 _ 或缺失分支 |
| E0308 | 类型不一致 | match 分支返回不同类型 | 统一返回类型 |
| 方向错误 | if let 方向反 | 值在左边,模式在右边 | 模式左,值右 |
小结
本章核心:
- match 比 switch 更安全
- match 必须穷尽所有情况
- match 分支返回值类型必须一致
- if let 简化单一模式匹配
- 编译器错误是最好的学习线索
练习题
详见:练习题