Rust 枚举 (enum) 完整核心知识点

25 阅读5分钟

Rust 枚举 (enum) 完整核心知识点

涵盖:基础定义、match 匹配、内存布局、递归枚举、易错点等
特点:由浅入深,含易错点、内存、递归、匹配


目录

  1. 基础定义:枚举是什么
  2. 枚举值创建与使用
  3. [核心:match 模式匹配](#三核心 match 模式匹配)
  4. 枚举绑定值所有权规则
  5. 枚举内存布局
  6. [判别式 discriminant](#六判别式 discriminant)
  7. 常用标准库枚举
  8. [枚举实现方法 impl](#八枚举实现方法 impl)
  9. 模式匹配进阶语法
  10. 枚举 vs 结构体核心区别
  11. 高频易错点总结
  12. 速记口诀

一、基础定义:枚举是什么

枚举是自定义类型,一组互斥的变体,一个枚举实例同一时间只能是其中一种变体。

C 枚举仅数字,Rust 枚举强大在于:每个变体可以附带数据(单元、元组、结构体)。

三种变体写法

// 1. 单元变体(无数据,类似 C 枚举)
enum Color { Red, Green, Blue }

// 2. 元组变体(带多个不同类型数据)
enum Msg {
    Quit,              // 单元
    Move(i32, i32),    // 元组
    Write(String),
}

// 3. 结构体变体(命名字段)
enum FileEvent {
    Read { path: String, offset: u64 },
    Write { path: String, data: Vec<u8> },
}

二、枚举值创建与使用

let r = Color::Red;
let m = Msg::Move(10, 20);
let w = Msg::Write("hello".to_string());

let ev = FileEvent::Read {
    path: "/tmp/a.txt".into(),
    offset: 0
};

三、核心:match 模式匹配(枚举灵魂)

match 强制覆盖所有变体,无遗漏编译报错,穷尽检查。

fn print_msg(msg: Msg) {
    match msg {
        Msg::Quit => println!("退出"),
        Msg::Move(x, y) => println!("移动 {} {}", x, y),
        Msg::Write(s) => println!("写入:{}", s),
    }
}

简化匹配 if let

只关心某一个变体,不想写全 match:

if let Msg::Write(text) = m {
    println!("{}", text);
}
// 对应 while let 循环匹配迭代器枚举

四、枚举绑定值所有权规则

  1. 匹配时解构会转移所有权(枚举带 String/Vec 等堆类型)
  2. ref / & 借用避免转移:
let msg = Msg::Write("test".into());
match msg {
    Msg::Write(ref s) => println!("{}", s), // 借用,不拿走所有权
    _ => ()
}
// 等价写法:if let Msg::Write(s) = &msg

五、枚举内存布局(重点,结合栈/堆区分)

1. 无数据单元枚举(C 风格)

底层是判别标签 (discriminant),单字节/单机器字,全部存栈,极小。

enum BoolLike { True, False } // 内存仅一个标记

2. 带数据枚举

内存 = 判别标签 + 最大变体占用的内存(联合体 union)

情况内存位置
变体内部是纯栈值(i32、元组(i32,f32)、定长数组[u8;8]整个枚举实例放栈
变体包含 String/Vec/Box 等智能指针枚举本体在栈,字段存指针,真实数据在堆
超大变体、递归枚举必须用 Box 包裹,否则编译报错(无限尺寸)

3. 递归枚举(必考点)

直接嵌套自身会无限大小,编译失败,必须用 Box 把内部枚举放到堆

// 报错:无限大小类型
enum List { Cons(i32, List), Nil }

// 正确:Box 栈存指针,子节点堆分配
enum List {
    Cons(i32, Box<List>),
    Nil
}

六、判别式 discriminant(自定义数字值)

#[repr(u8/u16/u32/i32)] 指定底层存储类型,用于 FFI、与 C 交互:

#[repr(u8)]
enum Status {
    Ok = 0,
    Fail = 1,
    Timeout = 2
}
// 转为数字:Status::Ok as u8

不加 repr 时,Rust 自动选最小整数存标签,不保证数值。


七、常用标准库枚举(高频开发)

1. Option - 可选值(有无数据)

enum Option<T> { Some(T), None }
// 常用:Some(val) / None
// 方法:unwrap(), expect(), map(), or_else()

2. Result<T,E> - 错误处理

enum Result<T,E> { Ok(T), Err(E) }
// 文件、IO、函数返回必用;? 操作符仅作用于 Result

3. Ordering - cmp 比较返回

  • Less / Equal / Greater

八、枚举实现方法 impl

枚举和 struct 一样可以 impl 方法:

impl Msg {
    fn is_quit(&self) -> bool {
        matches!(self, Msg::Quit)
    }
}
let m = Msg::Quit;
println!("{}", m.is_quit());

工具宏 matches!(变量,变体) 快速返回 bool。


九、模式匹配进阶语法

1. _ 通配符

匹配剩余所有变体,放 match 最后。

2. 解构结构体变体

match ev {
    FileEvent::Read { path, .. } => println!("{}", path),
    _ => ()
}

3. 匹配守卫 if

match m {
    Msg::Move(x, y) if x > 0 => println!("右移"),
    _ => ()
}

十、枚举 vs 结构体核心区别

对比项structenum
字段关系同时拥有全部字段同一时间只有一种变体
语义描述描述一个事物的全部属性描述事物多种互斥形态
内存布局平铺所有字段标签 + 联合体(只存一种变体的数据)

十一、高频易错点总结

⚠️ 以下易错点需特别注意

  1. 递归枚举不加 Box → 编译报错,无限大小
  2. match 必须穷尽所有变体,漏一个直接报错(安全兜底)
  3. 解构带堆类型的枚举会转移所有权,需要借用就取引用 &enum 匹配
  4. 单元枚举可以 as 转数字,但无 repr 时数值不固定,FFI 必须加 #[repr]
  5. enum 本身默认栈分配;只有变体内部包含 Box/Vec/String 时,对应数据才存在堆
  6. 不存在"枚举整体放堆",只有 Box<Enum> 时,栈存指针、枚举本体在堆

十二、速记口诀

枚举变体分三种,单元元组结构体;
match 匹配必穷尽,if let 简化单分支;
递归嵌套要 Box,否则尺寸无限大;
标签加联合体存内存,带字符串数据进堆;
OptionResult 两大标准枚举,空值错误全靠它。