首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Rust专项——枚举与模式匹配——类型安全的强大工具

Rust专项——枚举与模式匹配——类型安全的强大工具

作者头像
红目香薰
发布2025-12-16 16:36:24
发布2025-12-16 16:36:24
2410
举报
文章被收录于专栏:CSDNToQQCodeCSDNToQQCode

枚举(Enum)是 Rust 中最强大的类型系统特性之一,它允许你定义一个类型,其值可以是多个变体(variants)中的一个。结合模式匹配(Pattern Matching),枚举提供了表达力强、类型安全的代码组织方式。


目录

  • 1. 什么是枚举?
  • 2. 枚举的定义与基本语法
  • 3. 枚举的变体类型
  • 4. Option 枚举:处理可能为空的值
  • 5. Result 枚举:处理错误
  • 6. match 表达式:强大的模式匹配
  • 7. if let:简洁的单分支匹配
  • 8. while let:循环中的模式匹配
  • 9. 模式守卫(Pattern Guards)
  • 10. 枚举的方法实现
  • 11. 枚举与泛型
  • 12. 枚举的所有权
  • 13. 实际应用示例
  • 14. 常见错误与解决方案
  • 15. 最佳实践
  • 16. 扩展练习

1. 什么是枚举?

**枚举(Enum)**是一种自定义数据类型,允许你定义一个值可以是多个命名变体(variants)之一的类型。枚举是 Rust 中表达"或"关系的强大工具。

为什么需要枚举?

假设我们要表示一个 IP 地址,它可以是 IPv4 或 IPv6:

代码语言:javascript
复制
// 不使用枚举:容易出错,不安全
let ip_version = 4;  // 4 或 6?其他数字呢?
let ip_address = "192.168.1.1";

// 使用枚举:类型安全,表达清晰
enum IpAddr {
    V4,
    V6,
}

枚举的优势:

  • 类型安全:编译器确保只能使用定义的变体
  • 表达力强:清晰表达"或"的关系
  • 模式匹配:配合 match 实现强大的控制流
  • 数据关联:变体可以携带数据

2. 枚举的定义与基本语法

2.1 基本定义
代码语言:javascript
复制
enum EnumName {
    Variant1,
    Variant2,
    Variant3,
}
2.2 简单示例
代码语言:javascript
复制
// 方向枚举
enum Direction {
    North,
    South,
    East,
    West,
}

// 颜色枚举
enum Color {
    Red,
    Green,
    Blue,
}

// HTTP 状态码枚举
enum HttpStatus {
    Ok,              // 200
    NotFound,        // 404
    InternalError,   // 500
}

fn main() {
    let direction = Direction::North;
    let color = Color::Red;
    let status = HttpStatus::Ok;
}
在这里插入图片描述
在这里插入图片描述
2.3 枚举的使用
代码语言:javascript
复制
enum Direction {
    North,
    South,
    East,
    West,
}

fn get_direction_name(dir: Direction) -> &'static str {
    match dir {
        Direction::North => "北",
        Direction::South => "南",
        Direction::East => "东",
        Direction::West => "西",
    }
}

fn main() {
    let dir = Direction::North;
    println!("方向: {}", get_direction_name(dir));
}
在这里插入图片描述
在这里插入图片描述

3. 枚举的变体类型

枚举的变体可以携带不同类型的数据,这使得枚举非常灵活。

3.1 无数据变体(Unit Variants)
代码语言:javascript
复制
enum Message {
    Quit,      // 无数据
    Move,      // 无数据
    Write,     // 无数据
}
3.2 元组变体(Tuple Variants)
代码语言:javascript
复制
enum Message {
    Quit,
    Move { x: i32, y: i32 },  // 命名字段(结构体变体)
    Write(String),            // 元组变体:携带 String
    ChangeColor(i32, i32, i32), // 元组变体:携带三个 i32
}

fn main() {
    let msg1 = Message::Quit;
    let msg2 = Message::Move { x: 10, y: 20 };
    let msg3 = Message::Write("Hello".to_string());
    let msg4 = Message::ChangeColor(255, 0, 0);
}
3.3 结构体变体(Struct Variants)
代码语言:javascript
复制
enum Message {
    Quit,
    Move { x: i32, y: i32 },  // 结构体变体:命名字段
    Write(String),
}

fn handle_message(msg: Message) {
    match msg {
        Message::Quit => println!("退出"),
        Message::Move { x, y } => {
            println!("移动到 ({}, {})", x, y);
        }
        Message::Write(text) => {
            println!("写入: {}", text);
        }
    }
}

fn main() {
    handle_message(Message::Move { x: 5, y: 10 });
    handle_message(Message::Write("Hello".to_string()));
}
3.4 混合使用
代码语言:javascript
复制
#[derive(Debug)]
enum WebEvent {
    // 无数据变体
    PageLoad,
    PageUnload,
    
    // 元组变体
    KeyPress(char),
    Paste(String),
    
    // 结构体变体
    Click { x: i64, y: i64 },
    MouseMove { x: i64, y: i64 },
}

fn inspect(event: WebEvent) {
    match event {
        WebEvent::PageLoad => println!("页面加载"),
        WebEvent::PageUnload => println!("页面卸载"),
        WebEvent::KeyPress(c) => println!("按键: {}", c),
        WebEvent::Paste(s) => println!("粘贴: {}", s),
        WebEvent::Click { x, y } => {
            println!("点击位置: ({}, {})", x, y);
        }
        WebEvent::MouseMove { x, y } => {
            println!("鼠标移动到: ({}, {})", x, y);
        }
    }
}

fn main() {
    inspect(WebEvent::PageLoad);
    inspect(WebEvent::KeyPress('a'));
    inspect(WebEvent::Paste("Hello".to_string()));
    inspect(WebEvent::Click { x: 100, y: 200 });
}

4. Option 枚举:处理可能为空的值

Option<T> 是 Rust 标准库中最重要的枚举之一,用于表示一个值可能存在或不存在。

4.1 Option 的定义
代码语言:javascript
复制
enum Option<T> {
    Some(T),  // 有值
    None,     // 无值
}
4.2 为什么需要 Option?

很多语言使用 nullnil 表示"无值",但 Rust 没有 null。相反,Rust 使用 Option<T> 来明确表示值可能不存在,这避免了空指针异常。

代码语言:javascript
复制
// ❌ 其他语言:可能返回 null
// fn get_user(id: u32) -> User? { ... }

// ✅ Rust:明确表示可能没有值
fn get_user(id: u32) -> Option<String> {
    if id == 1 {
        Some("Alice".to_string())
    } else {
        None
    }
}
4.3 Option 的基本使用
代码语言:javascript
复制
fn divide(a: f64, b: f64) -> Option<f64> {
    if b != 0.0 {
        Some(a / b)
    } else {
        None
    }
}

fn main() {
    let result1 = divide(10.0, 2.0);
    match result1 {
        Some(value) => println!("结果: {}", value),
        None => println!("除零错误"),
    }
    
    let result2 = divide(10.0, 0.0);
    match result2 {
        Some(value) => println!("结果: {}", value),
        None => println!("除零错误"),
    }
}
4.4 Option 的常用方法
unwrap() 和 expect()
代码语言:javascript
复制
fn main() {
    let some_value = Some(5);
    let value = some_value.unwrap();  // 如果为 None 会 panic
    println!("值: {}", value);
    
    let none_value: Option<i32> = None;
    // let value = none_value.unwrap();  // ❌ panic!
    
    // expect 允许自定义错误消息
    let value = some_value.expect("应该有值");
}

注意unwrap()expect() 在生产代码中应该谨慎使用,通常只在确定 Option 不为 None 时使用。

is_some() 和 is_none()
代码语言:javascript
复制
fn main() {
    let option = Some(5);
    
    if option.is_some() {
        println!("有值");
    }
    
    if option.is_none() {
        println!("无值");
    }
}
unwrap_or() 和 unwrap_or_else()
代码语言:javascript
复制
fn main() {
    let some_value = Some(5);
    let none_value: Option<i32> = None;
    
    // 如果为 None,返回默认值
    let value1 = some_value.unwrap_or(0);  // 5
    let value2 = none_value.unwrap_or(0);   // 0
    
    // 如果为 None,执行闭包计算默认值
    let value3 = none_value.unwrap_or_else(|| {
        println!("计算默认值");
        42
    });
}
map() 和 and_then()
代码语言:javascript
复制
fn main() {
    let some_number = Some(5);
    
    // map: 如果有值,应用函数
    let doubled = some_number.map(|x| x * 2);
    println!("{:?}", doubled);  // Some(10)
    
    let none_number: Option<i32> = None;
    let doubled = none_number.map(|x| x * 2);
    println!("{:?}", doubled);  // None
    
    // and_then: 链式操作,可能返回 None
    let result = some_number
        .map(|x| x * 2)
        .and_then(|x| if x > 10 { Some(x) } else { None });
    println!("{:?}", result);
}
使用 ? 运算符传播 None
代码语言:javascript
复制
fn find_user(id: u32) -> Option<String> {
    if id == 1 {
        Some("Alice".to_string())
    } else {
        None
    }
}

fn process_user(id: u32) -> Option<String> {
    let user = find_user(id)?;  // 如果是 None,直接返回 None
    Some(format!("处理用户: {}", user))
}

fn main() {
    match process_user(1) {
        Some(msg) => println!("{}", msg),
        None => println!("用户不存在"),
    }
}

5. Result 枚举:处理错误

Result<T, E> 是 Rust 中处理错误的标准方式,表示操作可能成功(返回 Ok(T))或失败(返回 Err(E))。

5.1 Result 的定义
代码语言:javascript
复制
enum Result<T, E> {
    Ok(T),   // 成功,包含值
    Err(E),  // 错误,包含错误信息
}
5.2 Result 的基本使用
代码语言:javascript
复制
use std::fs::File;

fn main() {
    let result = File::open("hello.txt");
    
    match result {
        Ok(file) => {
            println!("文件打开成功: {:?}", file);
        }
        Err(error) => {
            println!("文件打开失败: {}", error);
        }
    }
}
5.3 处理不同类型的错误
代码语言:javascript
复制
use std::fs::File;
use std::io::ErrorKind;

fn main() {
    let result = File::open("hello.txt");
    
    match result {
        Ok(file) => println!("文件打开成功"),
        Err(error) => match error.kind() {
            ErrorKind::NotFound => {
                println!("文件未找到,尝试创建");
                File::create("hello.txt").unwrap_or_else(|error| {
                    panic!("创建文件失败: {:?}", error);
                });
            }
            ErrorKind::PermissionDenied => {
                println!("权限不足");
            }
            other_error => {
                panic!("其他错误: {:?}", other_error);
            }
        }
    }
}
5.4 Result 的常用方法
unwrap() 和 expect()
代码语言:javascript
复制
fn main() {
    let result: Result<i32, &str> = Ok(5);
    let value = result.unwrap();  // 5
    
    let error: Result<i32, &str> = Err("错误");
    // let value = error.unwrap();  // ❌ panic!
    
    let value = result.expect("应该有值");
}
unwrap_or() 和 unwrap_or_else()
代码语言:javascript
复制
fn main() {
    let ok_result: Result<i32, &str> = Ok(5);
    let err_result: Result<i32, &str> = Err("错误");
    
    let value1 = ok_result.unwrap_or(0);   // 5
    let value2 = err_result.unwrap_or(0);   // 0
    
    let value3 = err_result.unwrap_or_else(|e| {
        println!("错误: {}", e);
        -1
    });  // -1
}
map() 和 map_err()
代码语言:javascript
复制
fn main() {
    let result: Result<i32, &str> = Ok(5);
    
    // map: 转换 Ok 的值
    let doubled = result.map(|x| x * 2);
    println!("{:?}", doubled);  // Ok(10)
    
    // map_err: 转换 Err 的值
    let error: Result<i32, &str> = Err("错误");
    let mapped = error.map_err(|e| format!("新错误: {}", e));
    println!("{:?}", mapped);
}
and_then() 和 or_else()
代码语言:javascript
复制
fn divide(a: f64, b: f64) -> Result<f64, String> {
    if b != 0.0 {
        Ok(a / b)
    } else {
        Err("除零错误".to_string())
    }
}

fn main() {
    // and_then: 链式操作,成功时继续
    let result = divide(10.0, 2.0)
        .and_then(|x| divide(x, 2.0));
    println!("{:?}", result);  // Ok(2.5)
    
    // or_else: 失败时执行
    let result = divide(10.0, 0.0)
        .or_else(|_| Ok(0.0));
    println!("{:?}", result);  // Ok(0.0)
}
使用 ? 运算符传播错误
代码语言:javascript
复制
use std::fs::File;
use std::io::{self, Read};

fn read_file(filename: &str) -> Result<String, io::Error> {
    let mut file = File::open(filename)?;  // 如果错误,直接返回
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;   // 如果错误,直接返回
    Ok(contents)
}

fn main() {
    match read_file("hello.txt") {
        Ok(contents) => println!("文件内容: {}", contents),
        Err(e) => println!("读取失败: {}", e),
    }
}

6. match 表达式:强大的模式匹配

match 是 Rust 中最强大的控制流结构,它允许你根据值的模式执行不同的代码。

6.1 match 的基本语法
代码语言:javascript
复制
match value {
    pattern1 => expression1,
    pattern2 => expression2,
    pattern3 => expression3,
    _ => default_expression,  // 通配符:匹配所有其他情况
}
6.2 简单枚举的匹配
代码语言:javascript
复制
enum Direction {
    North,
    South,
    East,
    West,
}

fn get_direction_name(dir: Direction) -> &'static str {
    match dir {
        Direction::North => "北",
        Direction::South => "南",
        Direction::East => "东",
        Direction::West => "西",
    }
}

fn main() {
    let dir = Direction::North;
    println!("{}", get_direction_name(dir));
}
6.3 match 必须穷尽所有可能
代码语言:javascript
复制
enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter,
}

fn value_in_cents(coin: Coin) -> u8 {
    match coin {
        Coin::Penny => 1,
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter => 25,
        // 如果缺少任何一个变体,编译错误
    }
}
6.4 匹配带数据的变体
代码语言:javascript
复制
enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}

fn handle_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() {
    handle_message(Message::Move { x: 10, y: 20 });
    handle_message(Message::Write("Hello".to_string()));
    handle_message(Message::ChangeColor(255, 0, 0));
}
6.5 匹配 Option
代码语言:javascript
复制
fn plus_one(x: Option<i32>) -> Option<i32> {
    match x {
        None => None,
        Some(i) => Some(i + 1),
    }
}

fn main() {
    let five = Some(5);
    let six = plus_one(five);
    let none = plus_one(None);
    
    println!("{:?}, {:?}", six, none);  // Some(6), None
}
6.6 匹配 Result
代码语言:javascript
复制
use std::fs::File;

fn main() {
    let result = File::open("hello.txt");
    
    match result {
        Ok(file) => {
            println!("文件打开成功: {:?}", file);
        }
        Err(error) => {
            println!("文件打开失败: {}", error);
        }
    }
}
6.7 使用通配符 _
代码语言:javascript
复制
fn main() {
    let value = 3u8;
    
    match value {
        1 => println!("一"),
        2 => println!("二"),
        3 => println!("三"),
        _ => println!("其他数字"),  // 匹配所有其他值
    }
}
6.8 匹配多个值
代码语言:javascript
复制
fn main() {
    let value = 5u8;
    
    match value {
        1 | 2 | 3 => println!("小数字"),
        4 | 5 | 6 => println!("中等数字"),
        7 | 8 | 9 => println!("大数字"),
        _ => println!("其他"),
    }
}
6.9 匹配范围
代码语言:javascript
复制
fn main() {
    let value = 25u8;
    
    match value {
        0..=20 => println!("0-20"),
        21..=50 => println!("21-50"),
        51..=100 => println!("51-100"),
        _ => println!("超出范围"),
    }
}
6.10 match 是表达式

match 是一个表达式,可以返回值:

代码语言:javascript
复制
fn main() {
    let value = Some(5);
    
    let result = match value {
        Some(x) => x * 2,
        None => 0,
    };
    
    println!("结果: {}", result);  // 10
}

7. if let:简洁的单分支匹配

if letmatch 的语法糖,用于只关心一个模式的情况。

7.1 基本语法
代码语言:javascript
复制
if let pattern = value {
    // 匹配成功时的代码
} else {
    // 匹配失败时的代码(可选)
}
7.2 匹配 Option
代码语言:javascript
复制
fn main() {
    let some_value = Some(5);
    
    // 使用 match
    match some_value {
        Some(x) => println!("值是: {}", x),
        None => (),
    }
    
    // 使用 if let(更简洁)
    if let Some(x) = some_value {
        println!("值是: {}", x);
    }
    
    let none_value: Option<i32> = None;
    if let Some(x) = none_value {
        println!("值是: {}", x);
    } else {
        println!("没有值");
    }
}
7.3 匹配 Result
代码语言:javascript
复制
use std::fs::File;

fn main() {
    let result = File::open("hello.txt");
    
    if let Ok(file) = result {
        println!("文件打开成功: {:?}", file);
    } else {
        println!("文件打开失败");
    }
}
7.4 匹配枚举变体
代码语言:javascript
复制
enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
}

fn main() {
    let msg = Message::Move { x: 10, y: 20 };
    
    if let Message::Move { x, y } = msg {
        println!("移动到 ({}, {})", x, y);
    }
}

8. while let:循环中的模式匹配

while let 允许在循环中使用模式匹配,当模式匹配时继续循环。

8.1 基本语法
代码语言:javascript
复制
while let pattern = value {
    // 循环体
}
8.2 处理栈
代码语言:javascript
复制
fn main() {
    let mut stack = Vec::new();
    
    stack.push(1);
    stack.push(2);
    stack.push(3);
    
    // 当 pop() 返回 Some 时继续循环
    while let Some(top) = stack.pop() {
        println!("弹出: {}", top);
    }
}
8.3 处理迭代器
代码语言:javascript
复制
fn main() {
    let v = vec![1, 2, 3];
    let mut iter = v.iter();
    
    // 当 next() 返回 Some 时继续
    while let Some(value) = iter.next() {
        println!("值: {}", value);
    }
}

9. 模式守卫(Pattern Guards)

模式守卫允许在匹配模式后添加额外的条件判断。

9.1 基本语法
代码语言:javascript
复制
match value {
    pattern if condition => expression,
    _ => default,
}
9.2 示例:数字范围判断
代码语言:javascript
复制
fn main() {
    let num = Some(5);
    
    match num {
        Some(x) if x < 5 => println!("小于 5"),
        Some(x) if x == 5 => println!("等于 5"),
        Some(x) if x > 5 => println!("大于 5"),
        Some(_) => println!("其他"),
        None => println!("无值"),
    }
}
9.3 示例:匹配多个条件
代码语言:javascript
复制
fn main() {
    let point = (3, 5);
    
    match point {
        (x, y) if x == y => println!("在对角线上"),
        (x, y) if x == 0 => println!("在 y 轴上"),
        (x, y) if y == 0 => println!("在 x 轴上"),
        (x, y) => println!("在点 ({}, {})", x, y),
    }
}
9.4 示例:使用外部变量
代码语言:javascript
复制
fn main() {
    let x = 4;
    let y = false;
    
    match x {
        4 | 5 | 6 if y => println!("是的"),
        _ => println!("不是"),
    }
}

10. 枚举的方法实现

枚举也可以像结构体一样实现方法。

10.1 基本方法
代码语言:javascript
复制
enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
}

impl Message {
    fn call(&self) {
        match self {
            Message::Quit => println!("退出"),
            Message::Move { x, y } => println!("移动到 ({}, {})", x, y),
            Message::Write(text) => println!("写入: {}", text),
        }
    }
}

fn main() {
    let msg = Message::Write("Hello".to_string());
    msg.call();
}
10.2 关联函数
代码语言:javascript
复制
enum Color {
    Red,
    Green,
    Blue,
}

impl Color {
    fn new_rgb(r: u8, g: u8, b: u8) -> String {
        format!("RGB({}, {}, {})", r, g, b)
    }
    
    fn name(&self) -> &'static str {
        match self {
            Color::Red => "红色",
            Color::Green => "绿色",
            Color::Blue => "蓝色",
        }
    }
}

fn main() {
    let color = Color::Red;
    println!("{}", color.name());
    println!("{}", Color::new_rgb(255, 0, 0));
}

11. 枚举与泛型

枚举可以像结构体一样使用泛型。

11.1 泛型枚举
代码语言:javascript
复制
enum Option<T> {
    Some(T),
    None,
}

enum Result<T, E> {
    Ok(T),
    Err(E),
}

fn main() {
    let some_int: Option<i32> = Some(5);
    let some_string: Option<String> = Some("Hello".to_string());
    let none: Option<i32> = None;
    
    let ok_result: Result<i32, &str> = Ok(5);
    let err_result: Result<i32, &str> = Err("错误");
}
11.2 自定义泛型枚举
代码语言:javascript
复制
enum MyResult<T, E> {
    Success(T),
    Failure(E),
}

impl<T, E> MyResult<T, E> {
    fn is_success(&self) -> bool {
        match self {
            MyResult::Success(_) => true,
            MyResult::Failure(_) => false,
        }
    }
}

fn main() {
    let result: MyResult<i32, &str> = MyResult::Success(42);
    println!("{}", result.is_success());
}

12. 枚举的所有权

枚举的所有权规则与结构体相同。

12.1 拥有所有权的字段
代码语言:javascript
复制
enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),  // 拥有 String 的所有权
}

fn main() {
    let text = "Hello".to_string();
    let msg = Message::Write(text);  // text 的所有权转移到 msg
    // println!("{}", text);  // ❌ 错误:text 已被移动
}
12.2 使用引用(需要生命周期)
代码语言:javascript
复制
// 注意:这需要生命周期注解
// enum Message<'a> {
//     Write(&'a str),
// }

在基础阶段,建议先使用拥有所有权的字段。


13. 实际应用示例

13.1 状态机实现
代码语言:javascript
复制
#[derive(Debug, Clone, Copy, PartialEq)]
enum State {
    Idle,
    Loading,
    Loaded,
    Error,
}

struct App {
    state: State,
}

impl App {
    fn new() -> Self {
        App { state: State::Idle }
    }
    
    fn start_loading(&mut self) {
        match self.state {
            State::Idle => {
                self.state = State::Loading;
                println!("开始加载...");
            }
            _ => println!("无法从当前状态开始加载"),
        }
    }
    
    fn finish_loading(&mut self) {
        match self.state {
            State::Loading => {
                self.state = State::Loaded;
                println!("加载完成");
            }
            _ => println!("不在加载状态"),
        }
    }
    
    fn set_error(&mut self) {
        self.state = State::Error;
        println!("发生错误");
    }
}

fn main() {
    let mut app = App::new();
    app.start_loading();
    app.finish_loading();
    app.set_error();
}
13.2 配置解析系统
代码语言:javascript
复制
#[derive(Debug)]
enum ConfigValue {
    String(String),
    Number(i32),
    Boolean(bool),
    List(Vec<String>),
}

struct Config {
    values: std::collections::HashMap<String, ConfigValue>,
}

impl Config {
    fn new() -> Self {
        Config {
            values: std::collections::HashMap::new(),
        }
    }
    
    fn set(&mut self, key: String, value: ConfigValue) {
        self.values.insert(key, value);
    }
    
    fn get_string(&self, key: &str) -> Option<&String> {
        match self.values.get(key) {
            Some(ConfigValue::String(s)) => Some(s),
            _ => None,
        }
    }
    
    fn get_number(&self, key: &str) -> Option<i32> {
        match self.values.get(key) {
            Some(ConfigValue::Number(n)) => Some(*n),
            _ => None,
        }
    }
}

fn main() {
    let mut config = Config::new();
    config.set("name".to_string(), ConfigValue::String("Alice".to_string()));
    config.set("age".to_string(), ConfigValue::Number(25));
    config.set("active".to_string(), ConfigValue::Boolean(true));
    
    if let Some(name) = config.get_string("name") {
        println!("名字: {}", name);
    }
    
    if let Some(age) = config.get_number("age") {
        println!("年龄: {}", age);
    }
}
13.3 表达式求值器
代码语言:javascript
复制
#[derive(Debug, Clone)]
enum Expression {
    Number(i32),
    Add(Box<Expression>, Box<Expression>),
    Subtract(Box<Expression>, Box<Expression>),
    Multiply(Box<Expression>, Box<Expression>),
    Divide(Box<Expression>, Box<Expression>),
}

impl Expression {
    fn eval(&self) -> Result<i32, String> {
        match self {
            Expression::Number(n) => Ok(*n),
            Expression::Add(left, right) => {
                Ok(left.eval()? + right.eval()?)
            }
            Expression::Subtract(left, right) => {
                Ok(left.eval()? - right.eval()?)
            }
            Expression::Multiply(left, right) => {
                Ok(left.eval()? * right.eval()?)
            }
            Expression::Divide(left, right) => {
                let left_val = left.eval()?;
                let right_val = right.eval()?;
                if right_val == 0 {
                    Err("除零错误".to_string())
                } else {
                    Ok(left_val / right_val)
                }
            }
        }
    }
}

fn main() {
    // (3 + 4) * 2
    let expr = Expression::Multiply(
        Box::new(Expression::Add(
            Box::new(Expression::Number(3)),
            Box::new(Expression::Number(4)),
        )),
        Box::new(Expression::Number(2)),
    );
    
    match expr.eval() {
        Ok(result) => println!("结果: {}", result),
        Err(e) => println!("错误: {}", e),
    }
}
13.4 游戏状态管理
代码语言:javascript
复制
#[derive(Debug, Clone, Copy, PartialEq)]
enum GameState {
    Menu,
    Playing,
    Paused,
    GameOver,
}

struct Game {
    state: GameState,
    score: u32,
}

impl Game {
    fn new() -> Self {
        Game {
            state: GameState::Menu,
            score: 0,
        }
    }
    
    fn start(&mut self) {
        match self.state {
            GameState::Menu => {
                self.state = GameState::Playing;
                println!("游戏开始!");
            }
            _ => println!("无法从当前状态开始游戏"),
        }
    }
    
    fn pause(&mut self) {
        match self.state {
            GameState::Playing => {
                self.state = GameState::Paused;
                println!("游戏暂停");
            }
            _ => println!("无法暂停"),
        }
    }
    
    fn resume(&mut self) {
        match self.state {
            GameState::Paused => {
                self.state = GameState::Playing;
                println!("游戏继续");
            }
            _ => println!("无法继续"),
        }
    }
    
    fn game_over(&mut self) {
        self.state = GameState::GameOver;
        println!("游戏结束!分数: {}", self.score);
    }
}

fn main() {
    let mut game = Game::new();
    game.start();
    game.pause();
    game.resume();
    game.game_over();
}

14. 常见错误与解决方案

错误1:match 没有覆盖所有情况
代码语言:javascript
复制
enum Direction {
    North,
    South,
    East,
    West,
}

fn get_direction(dir: Direction) -> &'static str {
    match dir {
        Direction::North => "北",
        Direction::South => "南",
        // ❌ 错误:缺少 East 和 West
    }
}

解决方案:添加所有变体或使用通配符:

代码语言:javascript
复制
fn get_direction(dir: Direction) -> &'static str {
    match dir {
        Direction::North => "北",
        Direction::South => "南",
        Direction::East => "东",
        Direction::West => "西",
    }
}
错误2:忘记处理 None
代码语言:javascript
复制
fn divide(a: f64, b: f64) -> Option<f64> {
    if b != 0.0 {
        Some(a / b)
    } else {
        None
    }
}

fn main() {
    let result = divide(10.0, 2.0);
    // ❌ 错误:未处理 None 的情况
    // println!("{}", result.unwrap());
    
    // ✅ 正确:使用 match 或 if let
    match result {
        Some(value) => println!("{}", value),
        None => println!("除零错误"),
    }
}
错误3:模式匹配时所有权问题
代码语言:javascript
复制
enum Message {
    Write(String),
}

fn main() {
    let msg = Message::Write("Hello".to_string());
    
    match msg {
        Message::Write(text) => {
            println!("{}", text);
        }
    }
    
    // ❌ 错误:msg 已被移动
    // match msg { ... }
}

解决方案:使用引用或克隆:

代码语言:javascript
复制
match &msg {
    Message::Write(text) => {
        println!("{}", text);
    }
}

15. 最佳实践

15.1 何时使用枚举
  • 多个互斥的状态:使用枚举表示状态机
  • 可能为空的值:使用 Option<T>
  • 可能失败的操作:使用 Result<T, E>
  • 类型安全的常量:使用枚举替代魔法数字
15.2 枚举设计原则
  1. 变体命名清晰:使用描述性的名称
  2. 合理使用数据:变体可以携带不同类型的数据
  3. 实现方法:为枚举实现有用的方法
  4. 派生 trait:使用 #[derive] 添加常用 trait
15.3 match vs if let
  • match:需要处理所有情况时使用
  • if let:只关心一个模式时使用
  • while let:循环中模式匹配时使用

16. 扩展练习

练习1:实现计算器

创建一个 Calculator 枚举,支持:

  • Number(i32):数字
  • Add, Subtract, Multiply, Divide:运算符

实现 eval() 方法计算表达式。

练习2:实现 HTTP 状态码枚举

创建一个 HttpStatus 枚举,包含常见的 HTTP 状态码:

  • Ok(200)
  • NotFound(404)
  • InternalError(500)

实现 code() 方法返回状态码,message() 方法返回描述。

练习3:实现文件系统节点

创建 Node 枚举表示文件系统:

  • File { name: String, size: u64 }
  • Directory { name: String, children: Vec<Node> }

实现 total_size() 方法计算总大小。

练习4:实现简单的 JSON 解析器

创建 JsonValue 枚举表示 JSON 值:

  • Null
  • Boolean(bool)
  • Number(f64)
  • String(String)
  • Array(Vec<JsonValue>)
  • Object(HashMap<String, JsonValue>)

总结

枚举是 Rust 中强大的类型系统特性,通过本章学习,你应该掌握:

枚举的定义和变体类型Option 和 Result 的使用match 表达式的模式匹配if let 和 while let 的简洁语法模式守卫的使用枚举的方法实现枚举与泛型

枚举和模式匹配是 Rust 的核心特性,它们让你能够写出既安全又表达力强的代码。下一章我们将通过实战项目综合运用结构体和枚举。


本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-12-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 目录
  • 1. 什么是枚举?
    • 为什么需要枚举?
  • 2. 枚举的定义与基本语法
    • 2.1 基本定义
    • 2.2 简单示例
    • 2.3 枚举的使用
  • 3. 枚举的变体类型
    • 3.1 无数据变体(Unit Variants)
    • 3.2 元组变体(Tuple Variants)
    • 3.3 结构体变体(Struct Variants)
    • 3.4 混合使用
  • 4. Option 枚举:处理可能为空的值
    • 4.1 Option 的定义
    • 4.2 为什么需要 Option?
    • 4.3 Option 的基本使用
    • 4.4 Option 的常用方法
      • unwrap() 和 expect()
      • is_some() 和 is_none()
      • unwrap_or() 和 unwrap_or_else()
      • map() 和 and_then()
      • 使用 ? 运算符传播 None
  • 5. Result 枚举:处理错误
    • 5.1 Result 的定义
    • 5.2 Result 的基本使用
    • 5.3 处理不同类型的错误
    • 5.4 Result 的常用方法
      • unwrap() 和 expect()
      • unwrap_or() 和 unwrap_or_else()
      • map() 和 map_err()
      • and_then() 和 or_else()
      • 使用 ? 运算符传播错误
  • 6. match 表达式:强大的模式匹配
    • 6.1 match 的基本语法
    • 6.2 简单枚举的匹配
    • 6.3 match 必须穷尽所有可能
    • 6.4 匹配带数据的变体
    • 6.5 匹配 Option
    • 6.6 匹配 Result
    • 6.7 使用通配符 _
    • 6.8 匹配多个值
    • 6.9 匹配范围
    • 6.10 match 是表达式
  • 7. if let:简洁的单分支匹配
    • 7.1 基本语法
    • 7.2 匹配 Option
    • 7.3 匹配 Result
    • 7.4 匹配枚举变体
  • 8. while let:循环中的模式匹配
    • 8.1 基本语法
    • 8.2 处理栈
    • 8.3 处理迭代器
  • 9. 模式守卫(Pattern Guards)
    • 9.1 基本语法
    • 9.2 示例:数字范围判断
    • 9.3 示例:匹配多个条件
    • 9.4 示例:使用外部变量
  • 10. 枚举的方法实现
    • 10.1 基本方法
    • 10.2 关联函数
  • 11. 枚举与泛型
    • 11.1 泛型枚举
    • 11.2 自定义泛型枚举
  • 12. 枚举的所有权
    • 12.1 拥有所有权的字段
    • 12.2 使用引用(需要生命周期)
  • 13. 实际应用示例
    • 13.1 状态机实现
    • 13.2 配置解析系统
    • 13.3 表达式求值器
    • 13.4 游戏状态管理
  • 14. 常见错误与解决方案
    • 错误1:match 没有覆盖所有情况
    • 错误2:忘记处理 None
    • 错误3:模式匹配时所有权问题
  • 15. 最佳实践
    • 15.1 何时使用枚举
    • 15.2 枚举设计原则
    • 15.3 match vs if let
  • 16. 扩展练习
    • 练习1:实现计算器
    • 练习2:实现 HTTP 状态码枚举
    • 练习3:实现文件系统节点
    • 练习4:实现简单的 JSON 解析器
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档