枚举:定义单一类型,但只能是若干种不同变体 (variant) 其中之一。
枚举变体支持三种形式:单元变体、元组结构体变体、命名结构体变体。
以前学的其他编程语言 C C++ JAVA C#等等,枚举的值都只有一种类型 整数,但是rust中,枚举还可以是元组 结构体,同时配合match,不仅仅可以完成匹配,还可以把匹配的值解构
基础定义
// 允许未使用代码,消除所有dead_code系列警告
#![allow(dead_code)]
// 枚举定义:WebEvent,代表网页事件
enum WebEvent {
// 1. 单元式变体(类似单元结构体,不带任何数据)
PageLoad,
PageUnload,
// 2. 元组变体(类似元组结构体,携带有序数据)
KeyPress(char),
Paste(String),
// 3. 命名字段变体(类似C风格结构体,具名字段)x y 表示点击坐标点
Click { x: i64, y: i64 },
}
fn main() {
// 构造不同枚举实例
let load = WebEvent::PageLoad;
let key = WebEvent::KeyPress('k');
let paste = WebEvent::Paste("hello".to_owned());
let click = WebEvent::Click { x: 10, y: 20 };
}Code language: Rust (rust)
知识点解析
- 枚举名称::变体名 是完整标识:
WebEvent::KeyPress - 三大变体:
- 单元变体:无附加数据
PageLoad - 元组变体:带括号、有序数据
KeyPress(char) - 结构体变体:大括号、命名字段
Click{x,y}
- 单元变体:无附加数据
- 重点:不同变体是完全独立类型
WebEvent::PageLoad≠WebEvent::PageUnloadWebEvent::KeyPress(char)≠WebEvent::Paste(String)虽然同属 WebEvent 枚举,但携带数据结构不一样。 to_owned():把字符串字面量&str转换成拥有所有权的String,满足枚举变体所有权要求。
匹配枚举和枚举变体
// 允许未使用代码,消除所有dead_code系列警告
#![allow(dead_code)]
enum WebEvent {
PageLoad,
PageUnload,
KeyPress(char),
Paste(String),
Click { x: i64, y: i64 },
}
// match 处理枚举,同时解构变体内部数据
fn inspect(event: WebEvent) {
match event {
// 单元变体:直接匹配
WebEvent::PageLoad => println!("页面加载"),
WebEvent::PageUnload => println!("页面关闭"),
// 元组变体解构:取出括号内的值绑定变量 c
WebEvent::KeyPress(c) => println!("按下按键:{}", c),
WebEvent::Paste(s) => println!("粘贴内容:{}", s),
// 结构体变体解构:提取命名字段 x,y
WebEvent::Click { x, y } => println!("点击坐标 x={},y={}", x, y),
}
}
fn main() {
let pressed = WebEvent::KeyPress('x');
let pasted = WebEvent::Paste("my text".to_owned());
let click = WebEvent::Click { x: 20, y: 80 };
let load = WebEvent::PageLoad;
let unload = WebEvent::PageUnload;
inspect(pressed);
inspect(pasted);
inspect(click);
inspect(load);
inspect(unload);
}
Code language: Rust (rust)
- match 是处理枚举最核心语法,要求穷尽所有变体(必须覆盖全部分支,否则编译报错)
- 枚举解构规则(和 struct/tuple 解构思想统一)
- 单元变体:无需解构,直接匹配
- 元组变体:
WebEvent::KeyPress(c)将内部 char 绑定给变量 c - 结构体变体:
WebEvent::Click{x,y}直接提取字段为变量
- 解构会获取变体内部数据的所有权
- match 每个分支使用
=>,分支代码多行需要大括号{}
Type Alias 类型别名
// 允许未使用代码,消除所有dead_code系列警告
#![allow(dead_code)]
// 定义原始枚举(名字很长)
enum VeryVerboseEnumOfThingsToDoWithNumbers {
Add,
Subtract,
}
// 类型别名:给长类型起简短名字
type Operations = VeryVerboseEnumOfThingsToDoWithNumbers;
fn main() {
// 可以直接使用别名::变体
let calc = Operations::Add;
// 原始写法依然合法,两种写法等价
let calc2 = VeryVerboseEnumOfThingsToDoWithNumbers::Subtract;
}
Code language: Rust (rust)
知识点解析
- 语法:
type 别名 = 原有类型; - 类型别名不会创建新类型!仅仅是缩写、别名
Operations和VeryVerboseEnumOfThingsToDoWithNumbers是同一个类型,完全互通 - 使用场景:原始类型名称太长、泛型类型繁琐,简化代码书写
impl 中 Self 别名
enum VeryVerboseEnumOfThingsToDoWithNumbers {
Add,
Subtract,
}
// 为枚举实现方法
impl VeryVerboseEnumOfThingsToDoWithNumbers {
// &self 代表当前枚举实例的引用
fn run(&self, x: i32, y: i32) -> i32 {
match self {
// Self 等价于 VeryVerboseEnumOfThingsToDoWithNumbers
Self::Add => x + y,
Self::Subtract => x - y,
}
}
}
fn main() {
let op = VeryVerboseEnumOfThingsToDoWithNumbers::Add;
println!("{}", op.run(10, 5));
let op2 = VeryVerboseEnumOfThingsToDoWithNumbers::Subtract;
println!("{}", op2.run(10, 5));
}
Code language: PHP (php)
- 在
impl 类型 {}代码块内部,Self 是内置类型别名,代表当前正在实现方法的类型 - 好处:
- 枚举 / 结构体名字很长时,不用反复抄写完整名称
- 后续修改原始类型名称,impl 内部代码无需改动
&self:方法借用枚举实例,不获取所有权,调用之后实例依然有效
区别
- struct:一个实例同时拥有全部字段
- enum:一个实例只能是众多变体里的其中一种,互斥
- 结构体:
Point{x,y} - 元组结构体:
Pair(a,b) - 枚举元组变体:
WebEvent::KeyPress(c) - 枚举结构体变体:
WebEvent::Click{x,y}
所有解构底层逻辑一致:匹配数据结构,把内部数据提取绑定到独立变量
Previous: 结构体 (struct)
Next: 枚举应用