枚举(enum)和模式匹配(match)是Rust中最强大的特性组合。Rust的枚举远不止其他语言的简单枚举——每个变体可以携带不同类型和数量的数据,配合match表达式,让你写出既安全又优雅的代码。
学习目标:掌握枚举定义与变体、match表达式穷尽性、if let简化、模式匹配各种用法
enum Direction {
Up,
Down,
Left,
Right,
}
fn move_player(dir: Direction) -> &'static str {
match dir {
Direction::Up => "向上移动 ⬆️",
Direction::Down => "向下移动 ⬇️",
Direction::Left => "向左移动 ⬅️",
Direction::Right => "向右移动 ➡️",
}
}
fn main() {
let d = Direction::Up;
println!("{}", move_player(d));
}
enum Shape {
Circle(f64), // 半径
Rectangle { width: f64, height: f64 }, // 命名字段
Triangle(f64, f64, f64), // 三条边
}
impl Shape {
fn area(&self) -> f64 {
match self {
Shape::Circle(r) => std::f64::consts::PI * r * r,
Shape::Rectangle { width, height } => width * height,
Shape::Triangle(a, b, c) => {
// 海伦公式
let s = (a + b + c) / 2.0;
(s * (s - a) * (s - b) * (s - c)).sqrt()
}
}
}
fn describe(&self) -> String {
match self {
Shape::Circle(r) => format!("圆形(r={})", r),
Shape::Rectangle { width, height } => format!("矩形({}×{})", width, height),
Shape::Triangle(a, b, c) => format!("三角形({},{},{})", a, b, c),
}
}
}
fn main() {
let shapes = vec![
Shape::Circle(5.0),
Shape::Rectangle { width: 4.0, height: 6.0 },
Shape::Triangle(3.0, 4.0, 5.0),
];
for shape in &shapes {
println!("{}: 面积={:.2}", shape.describe(), shape.area());
}
}
✅ 验证通过
Option<T>是Rust取代null的设计。它明确表示一个值可能存在或不存在:
enum Option<T> {
Some(T),
None,
}
fn find_user(id: u32) -> Option<String> {
if id == 1 {
Some(String::from("Alice"))
} else if id == 2 {
Some(String::from("Bob"))
} else {
None
}
}
fn main() {
// Option基本使用
let some_number: Option<i32> = Some(42);
let no_number: Option<i32> = None;
println!("{:?}", some_number);
println!("{:?}", no_number);
// match处理
let user = find_user(1);
match user {
Some(name) => println!("找到用户: {}", name),
None => println!("用户不存在"),
}
// unwrap —— 获取值,None则panic
// let name = find_user(99).unwrap(); // ❌ panic!
// unwrap_or —— 提供默认值
let name = find_user(99).unwrap_or(String::from("未知用户"));
println!("用户: {}", name);
// unwrap_or_else —— 懒计算默认值
let name = find_user(99).unwrap_or_else(|| {
println!("用户不存在,使用默认值");
String::from("Guest")
});
println!("用户: {}", name);
// map —— 转换Some中的值
let user_len = find_user(1).map(|name| name.len());
println!("名字长度: {:?}", user_len);
// and_then —— 链式Option操作
let result = find_user(1)
.and_then(|name| {
if name.starts_with('A') {
Some(format!("管理员: {}", name))
} else {
None
}
});
println!("管理员: {:?}", result);
// if let —— 只关心一种情况
if let Some(name) = find_user(1) {
println!("if_let找到: {}", name);
}
// while let —— 循环处理
let mut stack = vec![1, 2, 3];
while let Some(top) = stack.pop() {
println!("弹出: {}", top);
}
}
✅ 验证通过
enum Coin {
Penny,
Nickel,
Dime,
Quarter(UsState),
}
#[derive(Debug)]
enum UsState {
Alabama,
Alaska,
California,
}
fn value_in_cents(coin: &Coin) -> u32 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter(state) => {
println!("来自{:?}的25分硬币!", state);
25
}
}
}
fn main() {
let coins = vec![
Coin::Penny,
Coin::Quarter(UsState::Alaska),
Coin::Dime,
];
let mut total = 0;
for coin in &coins {
total += value_in_cents(coin);
}
println!("总计: {}分", total);
// match是表达式
let x = 5;
let description = match x {
0 => "零",
1..=10 => "个位数",
11..=99 => "两位数",
_ => "大数", // _ 通配符
};
println!("{}是{}", x, description);
// 解构匹配
let point = (3, -5);
match point {
(0, 0) => println!("原点"),
(x, 0) => println!("x轴上, x={}", x),
(0, y) => println!("y轴上, y={}", y),
(x, y) if x > 0 && y > 0 => println!("第一象限 ({}, {})", x, y),
(x, y) => println!("坐标 ({}, {})", x, y),
}
// 守卫(guard)条件
let num = 4;
match num {
n if n % 2 == 0 => println!("{}是偶数", n),
n => println!("{}是奇数", n),
}
// 绑定 @
let age = 25u32;
match age {
n @ 0..=12 => println!("儿童({}岁)", n),
n @ 13..=19 => println!("青少年({}岁)", n),
n @ 20..=59 => println!("成人({}岁)", n),
n => println!("老年({}岁)", n),
}
}
✅ 验证通过
_可以匹配剩余所有情况。
/// 简单算术表达式AST
#[derive(Debug, Clone)]
enum Expr {
Number(f64),
Add(Box<Expr>, Box<Expr>),
Sub(Box<Expr>, Box<Expr>),
Mul(Box<Expr>, Box<Expr>),
Div(Box<Expr>, Box<Expr>),
Neg(Box<Expr>),
}
impl Expr {
fn num(v: f64) -> Self { Expr::Number(v) }
fn add(l: Expr, r: Expr) -> Self { Expr::Add(Box::new(l), Box::new(r)) }
fn sub(l: Expr, r: Expr) -> Self { Expr::Sub(Box::new(l), Box::new(r)) }
fn mul(l: Expr, r: Expr) -> Self { Expr::Mul(Box::new(l), Box::new(r)) }
fn div(l: Expr, r: Expr) -> Self { Expr::Div(Box::new(l), Box::new(r)) }
fn neg(e: Expr) -> Self { Expr::Neg(Box::new(e)) }
/// 求值
fn eval(&self) -> Result<f64, String> {
match self {
Expr::Number(n) => Ok(*n),
Expr::Add(l, r) => Ok(l.eval()? + r.eval()?),
Expr::Sub(l, r) => Ok(l.eval()? - r.eval()?),
Expr::Mul(l, r) => Ok(l.eval()? * r.eval()?),
Expr::Div(l, r) => {
let divisor = r.eval()?;
if divisor == 0.0 {
Err("除零错误".to_string())
} else {
Ok(l.eval()? / divisor)
}
}
Expr::Neg(e) => Ok(-e.eval()?),
}
}
/// 转为字符串表示
fn to_string_pretty(&self) -> String {
match self {
Expr::Number(n) => format!("{}", n),
Expr::Add(l, r) => format!("({} + {})", l.to_string_pretty(), r.to_string_pretty()),
Expr::Sub(l, r) => format!("({} - {})", l.to_string_pretty(), r.to_string_pretty()),
Expr::Mul(l, r) => format!("({} * {})", l.to_string_pretty(), r.to_string_pretty()),
Expr::Div(l, r) => format!("({} / {})", l.to_string_pretty(), r.to_string_pretty()),
Expr::Neg(e) => format!("-{}", e.to_string_pretty()),
}
}
}
fn main() {
// 计算 (3 + 4) * (2 - 1)
let expr = Expr::mul(
Expr::add(Expr::num(3.0), Expr::num(4.0)),
Expr::sub(Expr::num(2.0), Expr::num(1.0)),
);
println!("表达式: {}", expr.to_string_pretty());
println!("结果: {:?}", expr.eval());
// 带除零检查
let div_zero = Expr::div(Expr::num(1.0), Expr::num(0.0));
println!("1/0: {:?}", div_zero.eval());
// 复杂表达式: -(5 * 3 + 2) / 17
let complex = Expr::div(
Expr::neg(Expr::add(Expr::mul(Expr::num(5.0), Expr::num(3.0)), Expr::num(2.0))),
Expr::num(17.0),
);
println!("复杂表达式: {}", complex.to_string_pretty());
println!("结果: {:?}", complex.eval());
}
✅ 验证通过
定义IpAddr枚举,包含V4(u8,u8,u8,u8)和V6(String)变体,实现display方法。
用枚举实现简单的单链表:enum List { Nil, Cons(i32, Box<List>) },实现push和sum方法。
定义Json枚举表示JSON值:Null、Bool(bool)、Number(f64)、String、Array、Object,实现递归pretty print。
🔒 下一课解锁:错误处理 —— Result与Option的完整掌握
use std::fmt;
// 类型状态模式:编译期保证状态转换
struct Draft;
struct PendingReview;
struct Published;
struct Post {
content: String,
_state: std::marker::PhantomData,
}
impl Post {
fn content(&self) -> &str { &self.content }
}
impl Post {
fn new() -> Self {
Post { content: String::new(), _state: std::marker::PhantomData }
}
fn add_text(&mut self, text: &str) { self.content.push_str(text); }
fn request_review(self) -> Post {
Post { content: self.content, _state: std::marker::PhantomData }
}
}
impl Post {
fn approve(self) -> Post {
Post { content: self.content, _state: std::marker::PhantomData }
}
fn reject(self) -> Post {
Post { content: self.content, _state: std::marker::PhantomData }
}
}
impl Post {
// 只有Published状态才能调用
}
fn main() {
let mut post = Post::new();
post.add_text("Rust的类型状态模式");
// post.content(); // ❌ Draft状态不能读取内容
let post = post.request_review();
// post.add_text("more"); // ❌ PendingReview不能添加文本
let post = post.approve();
println!("发布: {}", post.content()); // ✅ Published可以读取
// 类型状态在编译期保证:不可能在Draft状态发布
println!("状态机模式确保安全的操作序列 ✅");
}
✅ 验证通过