Skip to content

模式匹配

> 学习 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
fn main() {
    let maybe = Some(5);
    if let Some(x) = maybe {
        println!("值:{}", x);
    }
}
▶ Run

详细示例

rust
fn 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!("这不是硬币");
    }
}
▶ Run

实际应用场景

rust
fn 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);
        }
    }
}
▶ Run

match 表达式

match 语法

概念名称: match 全面模式匹配,必须穷尽所有情况。

语法结构:
┌──────────────────────────────────────┐
│  match 值 {                           │
│        ↑                              │
│        要匹配的表达式                  │
│                                       │
│      模式1 => 结果1,                   │
│      模式2 => 结果2,                   │
│      _      => 默认值,  ← 通配符       │
│  }                                    │
│                                       │
│  match x {                            │
│      0 => "零",                       │
│      1..=9 => "一位数",               │
│      _ => "其他",                     │
│  }                                    │
└──────────────────────────────────────┘

最简示例

rust
fn main() {
    let x = 5;
    let desc = match x {
        0 => "零",
        _ => "非零",
    };
    println!("{}", desc);
}
▶ Run

详细示例

rust
fn main() {
    let number = 42;

    match number {
        0 => println!("零"),
        1..=9 => println!("个位数"),
        10..=99 => println!("十位数"),
        100..=999 => println!("百位数"),
        _ => println!("其他"),  // _ 是通配符
    }
}
▶ Run

match 返回值

rust
fn main() {
    let number = 5;

    let description = match number {
        0 => "零",
        n if n > 0 && n < 10 => "小的正数",
        10..=99 => "两位数",
        _ => "很大的数",
    };

    println!("{}", description);
}
▶ Run

匹配 Option 和 Result

rust
fn 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),
    }
}
▶ Run

迭代器方法

rust
fn 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);
}
▶ Run

语言对比: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
// Rust:match 返回值
fn describe(x: i32) -> &'static str {
    match x {
        0 => "零",
        n if n > 0 => "正数",
        _ => "负数",
    }
}
▶ Run
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
// Rust:提取值
let opt = Some(42);
match opt {
    Some(x) => println!("值:{}", x),  // 提取 x
    None => println!("无值"),
}

// C/Java:需要额外代码提取值
▶ Run

常见错误诊断

错误 1:match 未穷尽所有情况

错误代码

rust
fn main() {
    let opt: Option<i32> = Some(5);
    
    match opt {
        Some(x) => println!("{}", x),
        // 缺少 None 处理
    }
}
▶ Run

编译器错误

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
// 方案 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!("其他情况"),  // ✅ 通配符
    }
}
▶ Run

错误 2:match 分支类型不一致

错误代码

rust
fn main() {
    let x = 5;
    
    let result = match x {
        0 => "零",        // 返回 &str
        1 => 42,          // 返回 i32
        _ => "其他",
    };
}
▶ Run

编译器错误

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
fn main() {
    let x = Some(5);
    if let Some(5) = x {  // ❌ 方向错误
        println!("匹配");
    }
}
▶ Run

编译器错误

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)  ❌

错误诊断速查

错误码错误类型原因快速解决
E0004match 未穷尽缺少情况处理添加 _ 或缺失分支
E0308类型不一致match 分支返回不同类型统一返回类型
方向错误if let 方向反值在左边,模式在右边模式左,值右

小结

本章核心:

  • match 比 switch 更安全
  • match 必须穷尽所有情况
  • match 分支返回值类型必须一致
  • if let 简化单一模式匹配
  • 编译器错误是最好的学习线索

练习题

详见:练习题