Skip to content

enums.rs

文件信息

  • 📄 原文件:02_enums.rs
  • 🔤 语言:rust

完整代码

rust
// ============================================================
//                      枚举与模式匹配
// ============================================================
// Rust 的枚举(enum)远比其他语言的枚举强大
// 每个变体可以携带不同类型和数量的数据
// 结合模式匹配(match),是 Rust 中最常用的模式之一
//
// 【类比】类似 TypeScript 的联合类型,但更安全

// ----------------------------------------------------------
// 1. 基本枚举
// ----------------------------------------------------------
#[derive(Debug)]
enum Direction {
    Up,
    Down,
    Left,
    Right,
}

// ----------------------------------------------------------
// 2. 携带数据的枚举
// ----------------------------------------------------------
// 每个变体可以有不同的数据类型
#[derive(Debug)]
enum Message {
    Quit,                       // 无数据
    Move { x: i32, y: i32 },   // 命名字段(类似结构体)
    Write(String),              // 单个 String
    ChangeColor(u8, u8, u8),    // 三个 u8(类似元组)
}

// ----------------------------------------------------------
// 3. 枚举也可以有方法
// ----------------------------------------------------------
impl Message {
    fn call(&self) {
        match self {
            Message::Quit => println!("退出"),
            Message::Move { x, y } => println!("移动到 ({}, {})", x, y),
            Message::Write(text) => println!("写入: {}", text),
            Message::ChangeColor(r, g, b) => println!("颜色: ({}, {}, {})", r, g, b),
        }
    }
}

// ----------------------------------------------------------
// 4. Option<T>(Rust 没有 null)
// ----------------------------------------------------------
// Rust 没有 null 值!用 Option<T> 表示"可能没有值"
// enum Option<T> {
//     Some(T),  // 有值
//     None,     // 无值
// }
// 【重要】Option 在标准库预导入,不需要 use
// 【优势】编译器强制你处理 None 的情况,避免空指针异常

// ----------------------------------------------------------
// 5. Result<T, E>(错误处理)
// ----------------------------------------------------------
// enum Result<T, E> {
//     Ok(T),   // 成功
//     Err(E),  // 失败
// }
// 详见 06-error-handling

fn main() {
    println!("=== 枚举与模式匹配 ===");

    // ----------------------------------------------------------
    // 基本枚举使用
    // ----------------------------------------------------------
    let dir = Direction::Up;
    println!("方向: {:?}", dir);

    // match 匹配枚举
    let description = match dir {
        Direction::Up => "上",
        Direction::Down => "下",
        Direction::Left => "左",
        Direction::Right => "右",
    };
    println!("方向描述: {}", description);

    // ----------------------------------------------------------
    // 携带数据的枚举
    // ----------------------------------------------------------
    let messages = vec![
        Message::Quit,
        Message::Move { x: 10, y: 20 },
        Message::Write(String::from("你好")),
        Message::ChangeColor(255, 128, 0),
    ];

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

    // ----------------------------------------------------------
    // Option<T> 使用
    // ----------------------------------------------------------
    println!("\n=== Option<T> ===");

    let some_number: Option<i32> = Some(42);
    let no_number: Option<i32> = None;

    println!("some_number: {:?}", some_number);
    println!("no_number: {:?}", no_number);

    // 【重要】不能直接使用 Option<T> 的值,必须先"解包"
    // let result = some_number + 1;  // 错误!不能对 Option<i32> 做运算

    // 方式1: match
    match some_number {
        Some(n) => println!("match: 值是 {}", n),
        None => println!("match: 没有值"),
    }

    // 方式2: if let(只关心 Some 的情况)
    if let Some(n) = some_number {
        println!("if let: 值是 {}", n);
    }

    // 方式3: unwrap(有值返回值,无值 panic)
    // 【警告】仅在确定有值时使用,或者在原型代码中
    println!("unwrap: {}", some_number.unwrap());

    // 方式4: unwrap_or(提供默认值)
    println!("unwrap_or: {}", no_number.unwrap_or(0));

    // 方式5: unwrap_or_else(惰性默认值)
    println!("unwrap_or_else: {}", no_number.unwrap_or_else(|| {
        // 复杂的默认值计算
        42
    }));

    // 方式6: map(转换 Some 中的值)
    let doubled = some_number.map(|n| n * 2);
    println!("map: {:?}", doubled); // Some(84)

    // 方式7: and_then(链式操作,类似 flatMap)
    let result = some_number
        .and_then(|n| if n > 0 { Some(n * 10) } else { None })
        .and_then(|n| Some(n + 1));
    println!("and_then: {:?}", result); // Some(421)

    // 方式8: ? 操作符(在返回 Option 的函数中使用)
    println!("查找: {:?}", find_in_array(&[10, 20, 30], 20));
    println!("查找: {:?}", find_in_array(&[10, 20, 30], 50));

    // ----------------------------------------------------------
    // 模式匹配进阶
    // ----------------------------------------------------------
    println!("\n=== 模式匹配进阶 ===");

    // 解构复杂数据
    let msg = Message::Move { x: 10, y: 20 };
    if let Message::Move { x, y } = msg {
        println!("解构: x={}, y={}", x, y);
    }

    // 匹配多种模式
    let x = 4;
    match x {
        1 | 2 => println!("一或二"),
        3..=5 => println!("三到五"),  // 范围匹配
        _ => println!("其他"),
    }

    // 匹配守卫(Match Guard)
    let num = Some(4);
    match num {
        Some(x) if x < 0 => println!("负数: {}", x),
        Some(0) => println!("零"),
        Some(x) if x > 0 => println!("正数: {}", x),
        Some(_) => unreachable!(),
        None => println!("无值"),
    }

    // @ 绑定(匹配并捕获值)
    let age = 25;
    match age {
        n @ 0..=12 => println!("儿童, 年龄 {}", n),
        n @ 13..=17 => println!("青少年, 年龄 {}", n),
        n @ 18..=64 => println!("成人, 年龄 {}", n),
        n => println!("老年, 年龄 {}", n),
    }

    // 解构嵌套
    let points = vec![(0, 0), (1, 5), (10, -3)];
    for &(x, y) in &points {
        match (x, y) {
            (0, 0) => println!("在原点"),
            (x, 0) => println!("在 x 轴: x={}", x),
            (0, y) => println!("在 y 轴: y={}", y),
            (x, y) => println!("在 ({}, {})", x, y),
        }
    }

    // ----------------------------------------------------------
    // 实用枚举示例
    // ----------------------------------------------------------
    println!("\n=== 实用示例 ===");

    // 用枚举表示 JSON 值
    let json_data = vec![
        JsonValue::Null,
        JsonValue::Bool(true),
        JsonValue::Number(42.0),
        JsonValue::Str(String::from("hello")),
        JsonValue::Array(vec![
            JsonValue::Number(1.0),
            JsonValue::Number(2.0),
        ]),
    ];

    for value in &json_data {
        println!("  {}", value.to_string());
    }

    // 用枚举表示状态机
    let mut state = TrafficLight::Red;
    for _ in 0..6 {
        println!("交通灯: {:?} (等待 {} 秒)", state, state.duration());
        state = state.next();
    }

    println!("\n=== 枚举与模式匹配结束 ===");
}

// ----------------------------------------------------------
// Option 与 ? 操作符
// ----------------------------------------------------------
fn find_in_array(arr: &[i32], target: i32) -> Option<usize> {
    for (i, &val) in arr.iter().enumerate() {
        if val == target {
            return Some(i);
        }
    }
    None
}

// ----------------------------------------------------------
// 实用枚举:JSON 值
// ----------------------------------------------------------
#[derive(Debug)]
enum JsonValue {
    Null,
    Bool(bool),
    Number(f64),
    Str(String),
    Array(Vec<JsonValue>),
}

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

// ----------------------------------------------------------
// 实用枚举:状态机
// ----------------------------------------------------------
#[derive(Debug)]
enum TrafficLight {
    Red,
    Yellow,
    Green,
}

impl TrafficLight {
    fn duration(&self) -> u32 {
        match self {
            TrafficLight::Red => 60,
            TrafficLight::Yellow => 5,
            TrafficLight::Green => 45,
        }
    }

    fn next(self) -> TrafficLight {
        match self {
            TrafficLight::Red => TrafficLight::Green,
            TrafficLight::Green => TrafficLight::Yellow,
            TrafficLight::Yellow => TrafficLight::Red,
        }
    }
}

💬 讨论

使用 GitHub 账号登录后即可参与讨论

基于 MIT 许可发布