Rust 枚举 (enum) 完整核心知识点
涵盖 :基础定义、match 匹配、内存布局、递归枚举、易错点等
特点:由浅入深,含易错点、内存、递归、匹配
目录
- 基础定义:枚举是什么
- 枚举值创建与使用
- 核心:match 模式匹配(#三核心 match 模式匹配)
- 枚举绑定值所有权规则
- 枚举内存布局
- 判别式 discriminant(#六判别式 discriminant)
- 常用标准库枚举
- 枚举实现方法 impl(#八枚举实现方法 impl)
- 模式匹配进阶语法
- [枚举 vs 结构体核心区别](#枚举 vs 结构体核心区别 "#%E5%8D%81%E6%9E%9A%E4%B8%BE-vs-%E7%BB%93%E6%9E%84%E4%BD%93%E6%A0%B8%E5%BF%83%E5%8C%BA%E5%88%AB")
- 高频易错点总结
- 速记口诀
一、基础定义:枚举是什么
枚举是自定义类型,一组互斥的变体,一个枚举实例同一时间只能是其中一种变体。
C 枚举仅数字,Rust 枚举强大在于:每个变体可以附带数据(单元、元组、结构体)。
三种变体写法
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> },
}
二、枚举值创建与使用
rust
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 强制覆盖所有变体,无遗漏编译报错,穷尽检查。
rust
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:
rust
if let Msg::Write(text) = m {
println!("{}", text);
}
// 对应 while let 循环匹配迭代器枚举
四、枚举绑定值所有权规则
- 匹配时解构会转移所有权 (枚举带
String/Vec等堆类型) - 用
ref/&借用避免转移:
rust
let msg = Msg::Write("test".into());
match msg {
Msg::Write(ref s) => println!("{}", s), // 借用,不拿走所有权
_ => ()
}
// 等价写法:if let Msg::Write(s) = &msg
五、枚举内存布局(重点,结合栈/堆区分)
1. 无数据单元枚举(C 风格)
底层是判别标签 (discriminant),单字节/单机器字,全部存栈,极小。
rust
enum BoolLike { True, False } // 内存仅一个标记
2. 带数据枚举
内存 = 判别标签 + 最大变体占用的内存(联合体 union)
| 情况 | 内存位置 |
|---|---|
变体内部是纯栈值(i32、元组(i32,f32)、定长数组[u8;8]) |
整个枚举实例放栈 |
变体包含 String/Vec/Box 等智能指针 |
枚举本体在栈,字段存指针,真实数据在堆 |
| 超大变体、递归枚举 | 必须用 Box 包裹,否则编译报错(无限尺寸) |
3. 递归枚举(必考点)
直接嵌套自身会无限大小,编译失败,必须用 Box 把内部枚举放到堆:
rust
// 报错:无限大小类型
enum List { Cons(i32, List), Nil }
// 正确:Box 栈存指针,子节点堆分配
enum List {
Cons(i32, Box<List>),
Nil
}
六、判别式 discriminant(自定义数字值)
#[repr(u8/u16/u32/i32)] 指定底层存储类型,用于 FFI、与 C 交互:
rust
#[repr(u8)]
enum Status {
Ok = 0,
Fail = 1,
Timeout = 2
}
// 转为数字:Status::Ok as u8
不加 repr 时,Rust 自动选最小整数存标签,不保证数值。
七、常用标准库枚举(高频开发)
1. Option - 可选值(有无数据)
rust
enum Option<T> { Some(T), None }
// 常用:Some(val) / None
// 方法:unwrap(), expect(), map(), or_else()
2. Result<T,E> - 错误处理
rust
enum Result<T,E> { Ok(T), Err(E) }
// 文件、IO、函数返回必用;? 操作符仅作用于 Result
3. Ordering - cmp 比较返回
Less/Equal/Greater
八、枚举实现方法 impl
枚举和 struct 一样可以 impl 方法:
rust
impl Msg {
fn is_quit(&self) -> bool {
matches!(self, Msg::Quit)
}
}
let m = Msg::Quit;
println!("{}", m.is_quit());
工具宏 matches!(变量,变体) 快速返回 bool。
九、模式匹配进阶语法
1. _ 通配符
匹配剩余所有变体,放 match 最后。
2. 解构结构体变体
rust
match ev {
FileEvent::Read { path, .. } => println!("{}", path),
_ => ()
}
3. 匹配守卫 if
rust
match m {
Msg::Move(x, y) if x > 0 => println!("右移"),
_ => ()
}
十、枚举 vs 结构体核心区别
| 对比项 | struct | enum |
|---|---|---|
| 字段关系 | 同时拥有全部字段 | 同一时间只有一种变体 |
| 语义描述 | 描述一个事物的全部属性 | 描述事物多种互斥形态 |
| 内存布局 | 平铺所有字段 | 标签 + 联合体(只存一种变体的数据) |
十一、高频易错点总结
⚠️ 以下易错点需特别注意
- 递归枚举不加 Box → 编译报错,无限大小
- match 必须穷尽所有变体,漏一个直接报错(安全兜底)
- 解构带堆类型的枚举会转移所有权 ,需要借用就取引用
&enum匹配 - 单元枚举可以 as 转数字 ,但无
repr时数值不固定,FFI 必须加#[repr] - enum 本身默认栈分配 ;只有变体内部包含
Box/Vec/String时,对应数据才存在堆 - 不存在"枚举整体放堆" ,只有
Box<Enum>时,栈存指针、枚举本体在堆
十二、速记口诀
rust
枚举变体分三种,单元元组结构体;
match 匹配必穷尽,if let 简化单分支;
递归嵌套要 Box,否则尺寸无限大;
标签加联合体存内存,带字符串数据进堆;
Option、Result 两大标准枚举,空值错误全靠它。