强曰为道

与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

第08章:枚举与模式匹配

第08章:枚举与模式匹配

8.1 枚举基础

简单枚举

#[derive(Debug)]
enum Season {
    Spring,
    Summer,
    Autumn,
    Winter,
}

fn main() {
    let s = Season::Summer;
    println!("当前季节: {:?}", s);

    // 使用 match 匹配
    let temperature = match s {
        Season::Spring => "温暖",
        Season::Summer => "炎热",
        Season::Autumn => "凉爽",
        Season::Winter => "寒冷",
    };
    println!("温度: {}", temperature);
}

带数据的枚举

#[derive(Debug)]
enum Message {
    Quit,                              // 无数据
    Move { x: i32, y: i32 },          // 命名字段
    Write(String),                     // 包含 String
    Color(u8, u8, u8),                 // 包含三个 u8
    Resize { width: u32, height: u32 },
}

impl Message {
    fn process(&self) {
        match self {
            Message::Quit => println!("退出"),
            Message::Move { x, y } => println!("移动到 ({}, {})", x, y),
            Message::Write(text) => println!("写入: {}", text),
            Message::Color(r, g, b) => println!("颜色: #{:02X}{:02X}{:02X}", r, g, b),
            Message::Resize { width, height } => println!("调整大小: {}x{}", width, height),
        }
    }
}

fn main() {
    let messages = vec![
        Message::Quit,
        Message::Move { x: 10, y: 20 },
        Message::Write(String::from("hello")),
        Message::Color(255, 128, 0),
        Message::Resize { width: 800, height: 600 },
    ];

    for msg in &messages {
        msg.process();
    }
}

8.2 Option 详解

Rust 没有 null,用 Option<T> 表示可能缺失的值:

enum Option<T> {
    Some(T),  // 有值
    None,     // 无值
}

基本用法

fn find_user(id: u32) -> Option<String> {
    match id {
        1 => Some(String::from("Alice")),
        2 => Some(String::from("Bob")),
        _ => None,
    }
}

fn main() {
    for id in 1..=3 {
        match find_user(id) {
            Some(name) => println!("用户 {}: {}", id, name),
            None => println!("用户 {}: 未找到", id),
        }
    }
}

Option 常用方法

fn main() {
    let some: Option<i32> = Some(42);
    let none: Option<i32> = None;

    // unwrap_or: 提供默认值
    println!("{}", some.unwrap_or(0));     // 42
    println!("{}", none.unwrap_or(0));     // 0

    // unwrap_or_else: 惰性计算默认值
    println!("{}", none.unwrap_or_else(|| {
        println!("计算默认值...");
        100
    }));

    // map: 转换 Some 中的值
    let doubled = some.map(|x| x * 2);
    println!("{:?}", doubled); // Some(84)

    // and_then (flat_map)
    let result = some.and_then(|x| {
        if x > 0 { Some(x * 2) } else { None }
    });
    println!("{:?}", result); // Some(84)

    // filter
    let filtered = some.filter(|&x| x > 50);
    println!("{:?}", filtered); // None (42 < 50)

    // is_some / is_none
    println!("some is some: {}", some.is_some()); // true
    println!("none is none: {}", none.is_none()); // true

    // unwrap: 直接取值,None 时 panic
    println!("{}", some.unwrap()); // 42
    // none.unwrap(); // panic!

    // expect: 带消息的 unwrap
    println!("{}", some.expect("应该有值"));
    // none.expect("应该有值"); // panic: 应该有值

    // zip: 组合两个 Option
    let a = Some(1);
    let b = Some("hello");
    let zipped = a.zip(b);
    println!("{:?}", zipped); // Some((1, "hello"))
}

Option 转换

fn main() {
    // ok_or: Option<T> → Result<T, E>
    let opt: Option<i32> = Some(42);
    let res: Result<i32, &str> = opt.ok_or("值不存在");
    println!("{:?}", res); // Ok(42)

    // transpose: Option<Result<T, E>> → Result<Option<T>, E>
    let x: Option<Result<i32, &str>> = Some(Ok(42));
    let y: Result<Option<i32>, &str> = x.transpose();
    println!("{:?}", y); // Ok(Some(42))

    // flatten: Option<Option<T>> → Option<T>
    let nested: Option<Option<i32>> = Some(Some(42));
    let flat: Option<i32> = nested.flatten();
    println!("{:?}", flat); // Some(42)
}

8.3 Result<T, E> 详解

use std::num::ParseIntError;

fn parse_double(s: &str) -> Result<i64, ParseIntError> {
    let n = s.parse::<i64>()?;
    Ok(n * 2)
}

fn main() {
    // 成功
    match parse_double("21") {
        Ok(n) => println!("结果: {}", n),    // 42
        Err(e) => println!("错误: {}", e),
    }

    // 失败
    match parse_double("abc") {
        Ok(n) => println!("结果: {}", n),
        Err(e) => println!("错误: {}", e),   // invalid digit found in string
    }

    // 常用方法
    let ok: Result<i32, &str> = Ok(42);
    let err: Result<i32, &str> = Err("错误");

    println!("{:?}", ok.unwrap_or(0));         // 42
    println!("{:?}", err.unwrap_or(0));        // 0
    println!("{:?}", ok.map(|x| x * 2));      // Ok(84)
    println!("{:?}", err.map(|x| x * 2));     // Err("错误")
    println!("{:?}", ok.ok());                 // Some(42)
    println!("{:?}", err.err());               // Some("错误")
}

8.4 模式匹配详解

match 表达式

fn main() {
    let number = 13;

    // 基本匹配
    match number {
        1 => println!("一"),
        2 | 3 => println!("二或三"),
        4..=10 => println!("四到十"),
        _ => println!("其他: {}", number),
    }
}

匹配守卫(Match Guard)

fn main() {
    let pair = (2, -2);

    match pair {
        (x, y) if x == y => println!("相等"),
        (x, y) if x + y == 0 => println!("互为相反数"),
        (x, y) if x > 0 && y > 0 => println!("都在正区间"),
        _ => println!("其他情况"),
    }
}

解构结构体

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

fn main() {
    let p = Point { x: 0, y: 7 };

    match p {
        Point { x, y: 0 } => println!("在x轴上: x={}", x),
        Point { x: 0, y } => println!("在y轴上: y={}", y),
        Point { x, y } => println!("不在轴上: ({}, {})", x, y),
    }
}

解构枚举

enum Shape {
    Circle(f64),
    Rectangle { width: f64, height: f64 },
    Triangle(f64, f64, f64),
}

fn describe(shape: &Shape) {
    match shape {
        Shape::Circle(r) => println!("圆形, 半径={}", r),
        Shape::Rectangle { width, height } => {
            println!("矩形, {}×{}", width, height);
        }
        Shape::Triangle(a, b, c) => {
            println!("三角形, 边长={}, {}, {}", a, b, c);
        }
    }
}

fn main() {
    let shapes = vec![
        Shape::Circle(5.0),
        Shape::Rectangle { width: 10.0, height: 5.0 },
        Shape::Triangle(3.0, 4.0, 5.0),
    ];

    for shape in &shapes {
        describe(shape);
    }
}

解构嵌套类型

enum Color {
    Rgb(u8, u8, u8),
    Rgba(u8, u8, u8, u8),
}

enum Background {
    Solid(Color),
    Transparent(Color, f32),
}

fn main() {
    let bg = Background::Transparent(Color::Rgba(255, 0, 0, 128), 0.5);

    match bg {
        Background::Solid(Color::Rgb(r, g, b)) => {
            println!("不透明: ({}, {}, {})", r, g, b);
        }
        Background::Transparent(Color::Rgba(r, g, b, a), opacity) => {
            println!("透明: rgba({},{},{},{}) opacity={}", r, g, b, a, opacity);
        }
        _ => println!("其他"),
    }
}

解构引用

fn main() {
    let reference = &4;

    match reference {
        // &val 匹配引用并解引用
        &val => println!("解引用: {}", val),
    }

    // 或者使用 ref 模式
    match 4 {
        ref val => println!("引用: {}", val),
    }

    // 解构元组中的引用
    let pair = (&1, &2);
    match pair {
        (first, second) => println!("{} + {} = {}", first, second, first + second),
    }
}

8.5 if let 与 while let

if let

当只关心一种情况时,比 match 更简洁:

fn main() {
    let config_max: Option<u32> = Some(3);

    // match 方式
    match config_max {
        Some(max) => println!("最大值: {}", max),
        _ => (), // 必须处理 None
    }

    // if let 方式(更简洁)
    if let Some(max) = config_max {
        println!("最大值: {}", max);
    }

    // if let 带 else
    let value: Option<i32> = None;
    if let Some(v) = value {
        println!("值: {}", v);
    } else {
        println!("没有值");
    }
}

while let

循环直到模式不匹配:

fn main() {
    let mut stack = vec![1, 2, 3, 4, 5];

    // pop() 返回 Option<T>
    while let Some(top) = stack.pop() {
        println!("弹出: {}", top);
    }
    println!("栈已空: {:?}", stack);
}

matches! 宏

fn main() {
    let x = 42;

    // matches! 宏返回 bool
    let is_positive = matches!(x, 1..=100);
    println!("是正数(1-100): {}", is_positive);

    // 在迭代器中使用
    let nums = vec![Some(1), None, Some(3), None, Some(5)];
    let some_count = nums.iter().filter(|n| matches!(n, Some(_))).count();
    println!("Some 的数量: {}", some_count); // 3
}

8.6 业务场景示例

状态机

#[derive(Debug)]
enum TrafficLight {
    Red { remaining_secs: u32 },
    Yellow { remaining_secs: u32 },
    Green { remaining_secs: u32 },
}

impl TrafficLight {
    fn next(&self) -> TrafficLight {
        match self {
            TrafficLight::Red { .. } => TrafficLight::Green { remaining_secs: 60 },
            TrafficLight::Green { .. } => TrafficLight::Yellow { remaining_secs: 5 },
            TrafficLight::Yellow { .. } => TrafficLight::Red { remaining_secs: 90 },
        }
    }

    fn action(&self) -> &str {
        match self {
            TrafficLight::Red { .. } => "停车",
            TrafficLight::Yellow { .. } => "注意",
            TrafficLight::Green { .. } => "通行",
        }
    }

    fn remaining(&self) -> u32 {
        match self {
            TrafficLight::Red { remaining_secs }
            | TrafficLight::Yellow { remaining_secs }
            | TrafficLight::Green { remaining_secs } => *remaining_secs,
        }
    }
}

fn main() {
    let mut light = TrafficLight::Red { remaining_secs: 90 };

    for _ in 0..4 {
        println!("{:?}{} ({}秒)", light, light.action(), light.remaining());
        light = light.next();
    }
}

命令模式

#[derive(Debug)]
enum Command {
    List,
    Get { key: String },
    Set { key: String, value: String },
    Delete { key: String },
    Quit,
}

impl Command {
    fn parse(input: &str) -> Option<Command> {
        let parts: Vec<&str> = input.trim().splitn(3, ' ').collect();
        match parts.as_slice() {
            ["list"] => Some(Command::List),
            ["get", key] => Some(Command::Get { key: key.to_string() }),
            ["set", key, value] => Some(Command::Set {
                key: key.to_string(),
                value: value.to_string(),
            }),
            ["delete", key] => Some(Command::Delete { key: key.to_string() }),
            ["quit"] | ["exit"] => Some(Command::Quit),
            _ => None,
        }
    }
}

use std::collections::HashMap;

fn main() {
    let mut store: HashMap<String, String> = HashMap::new();

    // 模拟命令序列
    let inputs = vec![
        "set name Alice",
        "set age 30",
        "list",
        "get name",
        "delete age",
        "list",
    ];

    for input in inputs {
        println!("> {}", input);
        match Command::parse(input) {
            Some(Command::List) => {
                for (k, v) in &store {
                    println!("  {} = {}", k, v);
                }
            }
            Some(Command::Get { key }) => {
                match store.get(&key) {
                    Some(value) => println!("  {} = {}", key, value),
                    None => println!("  未找到: {}", key),
                }
            }
            Some(Command::Set { key, value }) => {
                store.insert(key, value);
                println!("  OK");
            }
            Some(Command::Delete { key }) => {
                store.remove(&key);
                println!("  已删除");
            }
            Some(Command::Quit) => {
                println!("  再见!");
                break;
            }
            None => println!("  未知命令"),
        }
    }
}

JSON 值模拟

#[derive(Debug, Clone)]
enum JsonValue {
    Null,
    Bool(bool),
    Number(f64),
    Str(String),
    Array(Vec<JsonValue>),
    Object(Vec<(String, JsonValue)>),
}

impl JsonValue {
    fn to_json(&self) -> String {
        match self {
            JsonValue::Null => "null".to_string(),
            JsonValue::Bool(b) => b.to_string(),
            JsonValue::Number(n) => format!("{}", n),
            JsonValue::Str(s) => format!("\"{}\"", s),
            JsonValue::Array(arr) => {
                let items: Vec<String> = arr.iter().map(|v| v.to_json()).collect();
                format!("[{}]", items.join(", "))
            }
            JsonValue::Object(pairs) => {
                let items: Vec<String> = pairs
                    .iter()
                    .map(|(k, v)| format!("\"{}\": {}", k, v.to_json()))
                    .collect();
                format!("{{{}}}", items.join(", "))
            }
        }
    }

    fn is_truthy(&self) -> bool {
        match self {
            JsonValue::Null => false,
            JsonValue::Bool(b) => *b,
            JsonValue::Number(n) => *n != 0.0,
            JsonValue::Str(s) => !s.is_empty(),
            JsonValue::Array(a) => !a.is_empty(),
            JsonValue::Object(o) => !o.is_empty(),
        }
    }
}

fn main() {
    let data = JsonValue::Object(vec![
        ("name".into(), JsonValue::Str("Alice".into())),
        ("age".into(), JsonValue::Number(30.0)),
        ("active".into(), JsonValue::Bool(true)),
        ("address".into(), JsonValue::Null),
        ("scores".into(), JsonValue::Array(vec![
            JsonValue::Number(95.0),
            JsonValue::Number(87.0),
            JsonValue::Number(92.0),
        ])),
    ]);

    println!("{}", data.to_json());
}

8.7 本章小结

要点说明
枚举使用 enum 定义,变体可以携带不同类型的数据
OptionSome(T)None,替代 null
ResultOk(T)Err(E),表示可能失败的操作
match穷尽匹配,必须覆盖所有变体
if let只关心一种情况时使用
while let循环直到模式不匹配
matches!返回 bool 的模式匹配宏
解构可以解构结构体、枚举、元组、引用等

扩展阅读

  1. Rust Book - 枚举 — 官方教程
  2. Rust Book - 模式匹配 — 模式匹配详解
  3. Option 文档 — Option API 参考