
枚举(Enum)是 Rust 中最强大的类型系统特性之一,它允许你定义一个类型,其值可以是多个变体(variants)中的一个。结合模式匹配(Pattern Matching),枚举提供了表达力强、类型安全的代码组织方式。
**枚举(Enum)**是一种自定义数据类型,允许你定义一个值可以是多个命名变体(variants)之一的类型。枚举是 Rust 中表达"或"关系的强大工具。
假设我们要表示一个 IP 地址,它可以是 IPv4 或 IPv6:
// 不使用枚举:容易出错,不安全
let ip_version = 4; // 4 或 6?其他数字呢?
let ip_address = "192.168.1.1";
// 使用枚举:类型安全,表达清晰
enum IpAddr {
V4,
V6,
}枚举的优势:
enum EnumName {
Variant1,
Variant2,
Variant3,
}// 方向枚举
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;
}
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));
}
枚举的变体可以携带不同类型的数据,这使得枚举非常灵活。
enum Message {
Quit, // 无数据
Move, // 无数据
Write, // 无数据
}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);
}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()));
}#[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 });
}Option<T> 是 Rust 标准库中最重要的枚举之一,用于表示一个值可能存在或不存在。
enum Option<T> {
Some(T), // 有值
None, // 无值
}很多语言使用 null 或 nil 表示"无值",但 Rust 没有 null。相反,Rust 使用 Option<T> 来明确表示值可能不存在,这避免了空指针异常。
// ❌ 其他语言:可能返回 null
// fn get_user(id: u32) -> User? { ... }
// ✅ Rust:明确表示可能没有值
fn get_user(id: u32) -> Option<String> {
if id == 1 {
Some("Alice".to_string())
} else {
None
}
}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!("除零错误"),
}
}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 时使用。
fn main() {
let option = Some(5);
if option.is_some() {
println!("有值");
}
if option.is_none() {
println!("无值");
}
}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
});
}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);
}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!("用户不存在"),
}
}Result<T, E> 是 Rust 中处理错误的标准方式,表示操作可能成功(返回 Ok(T))或失败(返回 Err(E))。
enum Result<T, E> {
Ok(T), // 成功,包含值
Err(E), // 错误,包含错误信息
}use std::fs::File;
fn main() {
let result = File::open("hello.txt");
match result {
Ok(file) => {
println!("文件打开成功: {:?}", file);
}
Err(error) => {
println!("文件打开失败: {}", error);
}
}
}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);
}
}
}
}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("应该有值");
}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
}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);
}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)
}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),
}
}match 是 Rust 中最强大的控制流结构,它允许你根据值的模式执行不同的代码。
match value {
pattern1 => expression1,
pattern2 => expression2,
pattern3 => expression3,
_ => default_expression, // 通配符:匹配所有其他情况
}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));
}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,
// 如果缺少任何一个变体,编译错误
}
}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));
}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
}use std::fs::File;
fn main() {
let result = File::open("hello.txt");
match result {
Ok(file) => {
println!("文件打开成功: {:?}", file);
}
Err(error) => {
println!("文件打开失败: {}", error);
}
}
}fn main() {
let value = 3u8;
match value {
1 => println!("一"),
2 => println!("二"),
3 => println!("三"),
_ => println!("其他数字"), // 匹配所有其他值
}
}fn main() {
let value = 5u8;
match value {
1 | 2 | 3 => println!("小数字"),
4 | 5 | 6 => println!("中等数字"),
7 | 8 | 9 => println!("大数字"),
_ => println!("其他"),
}
}fn main() {
let value = 25u8;
match value {
0..=20 => println!("0-20"),
21..=50 => println!("21-50"),
51..=100 => println!("51-100"),
_ => println!("超出范围"),
}
}match 是一个表达式,可以返回值:
fn main() {
let value = Some(5);
let result = match value {
Some(x) => x * 2,
None => 0,
};
println!("结果: {}", result); // 10
}if let 是 match 的语法糖,用于只关心一个模式的情况。
if let pattern = value {
// 匹配成功时的代码
} else {
// 匹配失败时的代码(可选)
}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!("没有值");
}
}use std::fs::File;
fn main() {
let result = File::open("hello.txt");
if let Ok(file) = result {
println!("文件打开成功: {:?}", file);
} else {
println!("文件打开失败");
}
}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);
}
}while let 允许在循环中使用模式匹配,当模式匹配时继续循环。
while let pattern = value {
// 循环体
}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);
}
}fn main() {
let v = vec![1, 2, 3];
let mut iter = v.iter();
// 当 next() 返回 Some 时继续
while let Some(value) = iter.next() {
println!("值: {}", value);
}
}模式守卫允许在匹配模式后添加额外的条件判断。
match value {
pattern if condition => expression,
_ => default,
}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!("无值"),
}
}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),
}
}fn main() {
let x = 4;
let y = false;
match x {
4 | 5 | 6 if y => println!("是的"),
_ => println!("不是"),
}
}枚举也可以像结构体一样实现方法。
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();
}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));
}枚举可以像结构体一样使用泛型。
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("错误");
}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());
}枚举的所有权规则与结构体相同。
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 已被移动
}// 注意:这需要生命周期注解
// enum Message<'a> {
// Write(&'a str),
// }在基础阶段,建议先使用拥有所有权的字段。
#[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();
}#[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);
}
}#[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),
}
}#[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();
}enum Direction {
North,
South,
East,
West,
}
fn get_direction(dir: Direction) -> &'static str {
match dir {
Direction::North => "北",
Direction::South => "南",
// ❌ 错误:缺少 East 和 West
}
}解决方案:添加所有变体或使用通配符:
fn get_direction(dir: Direction) -> &'static str {
match dir {
Direction::North => "北",
Direction::South => "南",
Direction::East => "东",
Direction::West => "西",
}
}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!("除零错误"),
}
}enum Message {
Write(String),
}
fn main() {
let msg = Message::Write("Hello".to_string());
match msg {
Message::Write(text) => {
println!("{}", text);
}
}
// ❌ 错误:msg 已被移动
// match msg { ... }
}解决方案:使用引用或克隆:
match &msg {
Message::Write(text) => {
println!("{}", text);
}
}Option<T>Result<T, E>#[derive] 添加常用 trait创建一个 Calculator 枚举,支持:
Number(i32):数字Add, Subtract, Multiply, Divide:运算符实现 eval() 方法计算表达式。
创建一个 HttpStatus 枚举,包含常见的 HTTP 状态码:
Ok(200)NotFound(404)InternalError(500)实现 code() 方法返回状态码,message() 方法返回描述。
创建 Node 枚举表示文件系统:
File { name: String, size: u64 }Directory { name: String, children: Vec<Node> }实现 total_size() 方法计算总大小。
创建 JsonValue 枚举表示 JSON 值:
NullBoolean(bool)Number(f64)String(String)Array(Vec<JsonValue>)Object(HashMap<String, JsonValue>)枚举是 Rust 中强大的类型系统特性,通过本章学习,你应该掌握:
✅ 枚举的定义和变体类型 ✅ Option 和 Result 的使用 ✅ match 表达式的模式匹配 ✅ if let 和 while let 的简洁语法 ✅ 模式守卫的使用 ✅ 枚举的方法实现 ✅ 枚举与泛型
枚举和模式匹配是 Rust 的核心特性,它们让你能够写出既安全又表达力强的代码。下一章我们将通过实战项目综合运用结构体和枚举。