目 录

📝 摘要

模式匹配(Pattern Matching)是 Rust 最强大的语言特性之一,它提供了一种简洁、类型安全的方式来处理复杂的数据结构。本文将深入剖析 Rust 模式匹配的各种形式、高级用法以及实战技巧,通过丰富的示例帮助读者掌握这一核心特性,写出更简洁、更安全的代码。


一、模式匹配基础

1.1 match 表达式

基本语法

fn main() {
    let number = 7;
    
    match number {
        1 => println!("一"),
        2 => println!("二"),
        3 | 4 | 5 => println!("三到五"),  // 多个模式
        6..=10 => println!("六到十"),     // 范围模式
        _ => println!("其他数字"),         // 捕获所有
    }
}

match 的核心特性

在这里插入图片描述

1.2 穷尽性检查(Exhaustiveness)

enum Color {
    Red,
    Green,
    Blue,
}

fn describe_color(color: Color) -> &'static str {
    match color {
        Color::Red => "红色",
        Color::Green => "绿色",
        Color::Blue => "蓝色",
        // ❌ 如果缺少任何分支,编译器会报错
    }
}

// 使用 _ 捕获其他情况
fn is_primary(color: Color) -> bool {
    match color {
        Color::Red | Color::Green | Color::Blue => true,
        _ => false,  // 虽然这里不会执行,但语法上需要
    }
}

1.3 match 作为表达式

fn main() {
    let number = 3;
    
    // match 返回值
    let description = match number {
        1 => "one",
        2 => "two",
        3 => "three",
        _ => "many",
    };
    
    println!("数字描述: {}", description);
    
    // 复杂表达式
    let result = match number {
        n if n < 0 => {
            println!("负数处理");
            n * -1
        },
        n if n == 0 => 0,
        n => {
            println!("正数处理");
            n * 2
        },
    };
    
    println!("结果: {}", result);
}

二、解构模式

2.1 解构结构体

struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let point = Point { x: 10, y: 20 };
    
    // 完整解构
    match point {
        Point { x: 0, y: 0 } => println!("原点"),
        Point { x: 0, y } => println!("在Y轴上,y = {}", y),
        Point { x, y: 0 } => println!("在X轴上,x = {}", x),
        Point { x, y } => println!("点({}, {})", x, y),
    }
    
    // 部分解构(使用 .. 忽略其他字段)
    match point {
        Point { x, .. } => println!("x = {}", x),
    }
}

// 嵌套结构体解构
struct Rectangle {
    top_left: Point,
    bottom_right: Point,
}

fn area(rect: Rectangle) -> i32 {
    match rect {
        Rectangle {
            top_left: Point { x: x1, y: y1 },
            bottom_right: Point { x: x2, y: y2 },
        } => (x2 - x1) * (y2 - y1),
    }
}

2.2 解构枚举

enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}

fn process_message(msg: Message) {
    match msg {
        Message::Quit => {
            println!("退出消息");
        },
        Message::Move { x, y } => {
            println!("移动到 ({}, {})", x, y);
        },
        Message::Write(text) => {
            println!("写入文本: {}", text);
        },
        Message::ChangeColor(r, g, b) => {
            println!("改变颜色为 RGB({}, {}, {})", r, g, b);
        },
    }
}

fn main() {
    let messages = vec![
        Message::Quit,
        Message::Move { x: 10, y: 20 },
        Message::Write(String::from("Hello")),
        Message::ChangeColor(255, 0, 0),
    ];
    
    for msg in messages {
        process_message(msg);
    }
}

2.3 解构元组

fn main() {
    let tuple = (1, "hello", 3.14);
    
    match tuple {
        (1, _, _) => println!("第一个元素是1"),
        (_, "world", _) => println!("第二个元素是world"),
        (x, y, z) => println!("元组内容: {}, {}, {}", x, y, z),
    }
    
    // 嵌套元组解构
    let nested = ((0, 1), (2, 3));
    
    match nested {
        ((0, y), _) => println!("第一个元组以0开始,第二个元素是{}", y),
        (_, (x, 3)) => println!("第二个元组以3结束,第一个元素是{}", x),
        _ => println!("其他情况"),
    }
}

2.4 解构引用

fn main() {
    let reference = &4;
    
    match reference {
        &val => println!("通过解构获得值: {}", val),
    }
    
    // 或者使用 ref 模式
    match reference {
        ref r => println!("获得引用: {}", r),
    }
    
    // 解构并创建引用
    let value = 5;
    
    match value {
        ref r => println!("创建引用: {}", r),
    }
}

// 复杂示例:解构 Option<&T>
fn print_option_reference(opt: Option<&i32>) {
    match opt {
        Some(&value) => println!("值: {}", value),
        Some(value) => println!("引用: {}", value),
        None => println!("无值"),
    }
}

三、模式守卫(Match Guards)

3.1 基础守卫

fn main() {
    let num = Some(4);
    
    match num {
        Some(x) if x < 5 => println!("小于5: {}", x),
        Some(x) => println!("大于等于5: {}", x),
        None => println!("无值"),
    }
}

// 多个条件
fn categorize_age(age: u32) -> &'static str {
    match age {
        n if n < 13 => "儿童",
        n if n < 20 => "青少年",
        n if n < 60 => "成年人",
        _ => "老年人",
    }
}

3.2 复杂守卫条件

#[derive(Debug)]
struct User {
    name: String,
    age: u32,
    active: bool,
}

fn check_user(user: User) {
    match user {
        User { name, age, active: true } if age >= 18 => {
            println!("✓ 成年活跃用户: {}", name);
        },
        User { name, age, active: false } if age >= 18 => {
            println!("⚠ 成年非活跃用户: {}", name);
        },
        User { name, age, .. } if age < 18 => {
            println!("❌ 未成年用户: {}", name);
        },
        _ => println!("其他情况"),
    }
}

fn main() {
    let users = vec![
        User { name: "Alice".to_string(), age: 25, active: true },
        User { name: "Bob".to_string(), age: 30, active: false },
        User { name: "Charlie".to_string(), age: 16, active: true },
    ];
    
    for user in users {
        check_user(user);
    }
}

3.3 外部变量使用

fn main() {
    let threshold = 10;
    let numbers = vec![5, 15, 8, 20, 3];
    
    for num in numbers {
        match num {
            n if n > threshold => println!("{} 大于阈值 {}", n, threshold),
            n if n == threshold => println!("{} 等于阈值", n),
            n => println!("{} 小于阈值", n),
        }
    }
}

四、@ 绑定

4.1 基础用法

fn main() {
    let value = 5;
    
    match value {
        n @ 1..=5 => println!("在1到5范围内: {}", n),
        n @ 6..=10 => println!("在6到10范围内: {}", n),
        _ => println!("其他范围"),
    }
}

// 枚举中使用 @
enum Status {
    Active(u32),
    Inactive,
}

fn check_status(status: Status) {
    match status {
        Status::Active(id @ 1..=100) => {
            println!("低ID活跃状态: {}", id);
        },
        Status::Active(id @ 101..=1000) => {
            println!("中ID活跃状态: {}", id);
        },
        Status::Active(id) => {
            println!("高ID活跃状态: {}", id);
        },
        Status::Inactive => {
            println!("非活跃状态");
        },
    }
}

4.2 复杂场景

struct Point {
    x: i32,
    y: i32,
}

fn analyze_point(point: Point) {
    match point {
        Point { x: 0, y: 0 } => println!("原点"),
        Point { x: 0, y } => println!("Y轴上: y = {}", y),
        Point { x, y: 0 } => println!("X轴上: x = {}", x),
        p @ Point { x: 1..=10, y: 1..=10 } => {
            println!("第一象限小区域: ({}, {})", p.x, p.y);
        },
        Point { x, y } => println!("其他点: ({}, {})", x, y),
    }
}

fn main() {
    analyze_point(Point { x: 0, y: 0 });
    analyze_point(Point { x: 5, y: 5 });
    analyze_point(Point { x: 20, y: 30 });
}

五、if let 和 while let

5.1 if let 简化模式匹配

fn main() {
    let favorite_color: Option<&str> = None;
    let is_tuesday = false;
    let age: Result<u8, _> = "34".parse();
    
    // 多个 if let 链式调用
    if let Some(color) = favorite_color {
        println!("使用你喜欢的颜色 {} 作为背景", color);
    } else if is_tuesday {
        println!("周二是绿色的日子");
    } else if let Ok(age) = age {
        if age > 30 {
            println!("使用紫色作为背景");
        } else {
            println!("使用橙色作为背景");
        }
    } else {
        println!("使用蓝色作为背景");
    }
}

// 实战示例:配置解析
struct Config {
    port: Option<u16>,
    host: Option<String>,
}

fn start_server(config: Config) {
    let port = if let Some(p) = config.port {
        p
    } else {
        8080
    };
    
    let host = if let Some(h) = config.host {
        h
    } else {
        "127.0.0.1".to_string()
    };
    
    println!("🚀 服务器启动在 {}:{}", host, port);
}

5.2 while let 循环

fn main() {
    let mut stack = Vec::new();
    
    stack.push(1);
    stack.push(2);
    stack.push(3);
    
    // while let 循环处理 Option
    while let Some(top) = stack.pop() {
        println!("弹出: {}", top);
    }
}

// 实战:迭代器处理
fn process_lines(text: &str) {
    let mut lines = text.lines();
    
    while let Some(line) = lines.next() {
        if line.starts_with("ERROR") {
            println!("❌ 错误日志: {}", line);
        } else if line.starts_with("WARN") {
            println!("⚠️  警告日志: {}", line);
        } else {
            println!("ℹ️  信息日志: {}", line);
        }
    }
}

六、函数参数中的模式

6.1 元组参数解构

fn print_coordinates(&(x, y): &(i32, i32)) {
    println!("坐标: ({}, {})", x, y);
}

fn calculate_distance(&(x1, y1): &(i32, i32), &(x2, y2): &(i32, i32)) -> f64 {
    let dx = (x2 - x1) as f64;
    let dy = (y2 - y1) as f64;
    (dx * dx + dy * dy).sqrt()
}

fn main() {
    let point = (3, 5);
    print_coordinates(&point);
    
    let p1 = (0, 0);
    let p2 = (3, 4);
    let distance = calculate_distance(&p1, &p2);
    println!("距离: {:.2}", distance);
}

6.2 结构体参数解构

struct User {
    username: String,
    email: String,
    active: bool,
}

fn greet_user(User { username, .. }: &User) {
    println!("你好, {}!", username);
}

fn is_active_admin(User { username, active, .. }: &User) -> bool {
    active && username == "admin"
}

fn main() {
    let user = User {
        username: String::from("admin"),
        email: String::from("admin@example.com"),
        active: true,
    };
    
    greet_user(&user);
    println!("是管理员? {}", is_active_admin(&user));
}

七、高级模式匹配技巧

7.1 or 模式

fn main() {
    let x = 1;
    
    match x {
        1 | 2 => println!("一或二"),
        3 | 4 | 5 => println!("三、四或五"),
        _ => println!("其他"),
    }
}

// 复杂 or 模式
enum HttpStatus {
    Ok,
    Created,
    BadRequest,
    Unauthorized,
    NotFound,
    InternalServerError,
}

fn is_success(status: HttpStatus) -> bool {
    matches!(status, HttpStatus::Ok | HttpStatus::Created)
}

fn is_client_error(status: HttpStatus) -> bool {
    matches!(
        status,
        HttpStatus::BadRequest | HttpStatus::Unauthorized | HttpStatus::NotFound
    )
}

7.2 matches! 宏

fn main() {
    let value = Some(5);
    
    // 使用 matches! 宏
    if matches!(value, Some(x) if x > 3) {
        println!("值大于3");
    }
    
    // 等价于
    let is_match = match value {
        Some(x) if x > 3 => true,
        _ => false,
    };
}

// 实战:验证器
fn is_valid_email(email: &str) -> bool {
    matches!(
        email.split('@').collect::<Vec<_>>().as_slice(),
        [_, domain] if domain.contains('.')
    )
}

fn main() {
    let emails = vec![
        "user@example.com",
        "invalid",
        "user@domain",
    ];
    
    for email in emails {
        println!("{}: {}", email, is_valid_email(email));
    }
}

7.3 切片模式

fn main() {
    let numbers = [1, 2, 3, 4, 5];
    
    match numbers.as_slice() {
        [] => println!("空数组"),
        [first] => println!("只有一个元素: {}", first),
        [first, second] => println!("两个元素: {}, {}", first, second),
        [first, .., last] => println!("首尾: {}, {}", first, last),
        _ => println!("其他情况"),
    }
}

// 更复杂的切片模式
fn analyze_data(data: &[i32]) {
    match data {
        [] => println!("无数据"),
        [x] => println!("单个数据点: {}", x),
        [x, y] => println!("两个数据点: {}, {}", x, y),
        [first, middle @ .., last] => {
            println!("首: {}, 尾: {}, 中间有{}个元素", 
                     first, last, middle.len());
        },
    }
}

fn main() {
    analyze_data(&[]);
    analyze_data(&[1]);
    analyze_data(&[1, 2]);
    analyze_data(&[1, 2, 3, 4, 5]);
}

八、实战案例

8.1 案例1:命令行参数解析器

enum Command {
    Add { x: i32, y: i32 },
    Subtract { x: i32, y: i32 },
    Multiply { x: i32, y: i32 },
    Divide { x: i32, y: i32 },
    Help,
}

fn parse_command(args: &[String]) -> Result<Command, String> {
    match args {
        [_, cmd, x, y] => {
            let x: i32 = x.parse().map_err(|_| "Invalid number")?;
            let y: i32 = y.parse().map_err(|_| "Invalid number")?;
            
            match cmd.as_str() {
                "add" => Ok(Command::Add { x, y }),
                "sub" => Ok(Command::Subtract { x, y }),
                "mul" => Ok(Command::Multiply { x, y }),
                "div" => Ok(Command::Divide { x, y }),
                _ => Err(format!("Unknown command: {}", cmd)),
            }
        },
        [_, cmd] if cmd == "help" => Ok(Command::Help),
        _ => Err("Usage: calc <command> [x] [y]".to_string()),
    }
}

fn execute_command(cmd: Command) -> Result<i32, String> {
    match cmd {
        Command::Add { x, y } => Ok(x + y),
        Command::Subtract { x, y } => Ok(x - y),
        Command::Multiply { x, y } => Ok(x * y),
        Command::Divide { x, y } if y != 0 => Ok(x / y),
        Command::Divide { .. } => Err("Division by zero".to_string()),
        Command::Help => {
            println!("Commands: add, sub, mul, div, help");
            Ok(0)
        },
    }
}

fn main() {
    let args: Vec<String> = std::env::args().collect();
    
    match parse_command(&args) {
        Ok(cmd) => match execute_command(cmd) {
            Ok(result) => println!("结果: {}", result),
            Err(e) => eprintln!("错误: {}", e),
        },
        Err(e) => eprintln!("解析错误: {}", e),
    }
}

8.2 案例2:JSON 解析器

use serde_json::Value;

fn analyze_json(value: &Value) {
    match value {
        Value::Null => println!("类型: Null"),
        
        Value::Bool(b) => println!("布尔值: {}", b),
        
        Value::Number(n) if n.is_i64() => {
            println!("整数: {}", n.as_i64().unwrap());
        },
        Value::Number(n) if n.is_f64() => {
            println!("浮点数: {}", n.as_f64().unwrap());
        },
        Value::Number(_) => println!("数字类型"),
        
        Value::String(s) if s.starts_with("http") => {
            println!("URL: {}", s);
        },
        Value::String(s) => println!("字符串: {}", s),
        
        Value::Array(arr) if arr.is_empty() => {
            println!("空数组");
        },
        Value::Array(arr) => {
            println!("数组 ({}个元素)", arr.len());
            for (i, item) in arr.iter().enumerate().take(3) {
                println!("  [{}]: {:?}", i, item);
            }
        },
        
        Value::Object(obj) if obj.is_empty() => {
            println!("空对象");
        },
        Value::Object(obj) => {
            println!("对象 ({}个字段)", obj.len());
            for (key, val) in obj.iter().take(3) {
                println!("  {}: {:?}", key, val);
            }
        },
    }
}

fn main() {
    let json_str = r#"
    {
        "name": "Alice",
        "age": 30,
        "url": "https://example.com",
        "active": true,
        "scores": [85, 90, 95]
    }
    "#;
    
    if let Ok(value) = serde_json::from_str::<Value>(json_str) {
        analyze_json(&value);
    }
}

8.3 案例3:状态机

#[derive(Debug)]
enum State {
    Idle,
    Connecting,
    Connected { session_id: String },
    Disconnecting,
    Error { message: String },
}

enum Event {
    Connect,
    Disconnect,
    ReceiveData(String),
    Timeout,
    Reset,
}

fn handle_event(state: State, event: Event) -> State {
    match (state, event) {
        // Idle 状态转换
        (State::Idle, Event::Connect) => {
            println!("开始连接...");
            State::Connecting
        },
        
        // Connecting 状态转换
        (State::Connecting, Event::ReceiveData(session_id)) => {
            println!("连接成功,会话ID: {}", session_id);
            State::Connected { session_id }
        },
        (State::Connecting, Event::Timeout) => {
            println!("连接超时");
            State::Error {
                message: "Connection timeout".to_string(),
            }
        },
        
        // Connected 状态转换
        (State::Connected { session_id }, Event::Disconnect) => {
            println!("断开连接,会话ID: {}", session_id);
            State::Disconnecting
        },
        (State::Connected { .. }, Event::ReceiveData(data)) => {
            println!("收到数据: {}", data);
            State::Connected {
                session_id: "active".to_string(),
            }
        },
        
        // Disconnecting 状态转换
        (State::Disconnecting, _) => {
            println!("断开完成");
            State::Idle
        },
        
        // Error 状态转换
        (State::Error { message }, Event::Reset) => {
            println!("重置错误: {}", message);
            State::Idle
        },
        
        // 无效转换
        (state, event) => {
            println!("⚠️  无效转换: {:?} + {:?}", state, event);
            state
        },
    }
}

fn main() {
    let mut state = State::Idle;
    
    let events = vec![
        Event::Connect,
        Event::ReceiveData("session-123".to_string()),
        Event::ReceiveData("Hello".to_string()),
        Event::Disconnect,
    ];
    
    for event in events {
        state = handle_event(state, event);
        println!("当前状态: {:?}\n", state);
    }
}

九、性能考量

9.1 模式匹配的性能

// ✓ 高效:编译为跳转表
fn classify_number(n: i32) -> &'static str {
    match n {
        0 => "零",
        1 => "一",
        2 => "二",
        3 => "三",
        4 => "四",
        _ => "其他",
    }
}

// ✓ 高效:单次比较
fn is_vowel(c: char) -> bool {
    matches!(c, 'a' | 'e' | 'i' | 'o' | 'u')
}

// ❌ 低效:多次字符串比较
fn is_vowel_slow(s: &str) -> bool {
    s == "a" || s == "e" || s == "i" || s == "o" || s == "u"
}

性能对比表

模式类型 时间复杂度 编译优化 适用场景
整数匹配 O(1) 跳转表 枚举值
范围匹配 O(1) 边界检查 连续区间
字符串匹配 O(n) 哈希表 少量字符串
结构体解构 O(1) 直接访问 数据提取

十、总结与讨论

模式匹配是 Rust 最优雅的特性之一,关键要点包括:

✅ 类型安全:编译期穷尽性检查
✅ 表达力强:简洁处理复杂逻辑
✅ 零开销:编译为高效机器码
✅ 可组合:支持嵌套和守卫

核心概念总结

在这里插入图片描述

讨论问题

  1. 模式匹配相比 if-else 有哪些优势?
  2. 何时应该使用 match vs if let
  3. 你在项目中遇到过哪些巧妙的模式匹配用法?

欢迎分享经验!💬


参考链接

  1. Rust Book - Patterns:https://doc.rust-lang.org/book/ch18-00-patterns.html
  2. Rust Reference - Patterns:https://doc.rust-lang.org/reference/patterns.html
  3. Pattern Matching RFC:https://github.com/rust-lang/rfcs/

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐