第08章:枚举与模式匹配
第08章:枚举与模式匹配
8.1 枚举基础
简单枚举
#[derive(Debug)]
enum Season {
Spring,
Summer,
Autumn,
Winter,
}
fn main() {
let s = Season::Summer;
println!("当前季节: {:?}", s);
// 使用 match 匹配
let temperature = match s {
Season::Spring => "温暖",
Season::Summer => "炎热",
Season::Autumn => "凉爽",
Season::Winter => "寒冷",
};
println!("温度: {}", temperature);
}
带数据的枚举
#[derive(Debug)]
enum Message {
Quit, // 无数据
Move { x: i32, y: i32 }, // 命名字段
Write(String), // 包含 String
Color(u8, u8, u8), // 包含三个 u8
Resize { width: u32, height: u32 },
}
impl Message {
fn process(&self) {
match self {
Message::Quit => println!("退出"),
Message::Move { x, y } => println!("移动到 ({}, {})", x, y),
Message::Write(text) => println!("写入: {}", text),
Message::Color(r, g, b) => println!("颜色: #{:02X}{:02X}{:02X}", r, g, b),
Message::Resize { width, height } => println!("调整大小: {}x{}", width, height),
}
}
}
fn main() {
let messages = vec![
Message::Quit,
Message::Move { x: 10, y: 20 },
Message::Write(String::from("hello")),
Message::Color(255, 128, 0),
Message::Resize { width: 800, height: 600 },
];
for msg in &messages {
msg.process();
}
}
8.2 Option 详解
Rust 没有 null,用 Option<T> 表示可能缺失的值:
enum Option<T> {
Some(T), // 有值
None, // 无值
}
基本用法
fn find_user(id: u32) -> Option<String> {
match id {
1 => Some(String::from("Alice")),
2 => Some(String::from("Bob")),
_ => None,
}
}
fn main() {
for id in 1..=3 {
match find_user(id) {
Some(name) => println!("用户 {}: {}", id, name),
None => println!("用户 {}: 未找到", id),
}
}
}
Option 常用方法
fn main() {
let some: Option<i32> = Some(42);
let none: Option<i32> = None;
// unwrap_or: 提供默认值
println!("{}", some.unwrap_or(0)); // 42
println!("{}", none.unwrap_or(0)); // 0
// unwrap_or_else: 惰性计算默认值
println!("{}", none.unwrap_or_else(|| {
println!("计算默认值...");
100
}));
// map: 转换 Some 中的值
let doubled = some.map(|x| x * 2);
println!("{:?}", doubled); // Some(84)
// and_then (flat_map)
let result = some.and_then(|x| {
if x > 0 { Some(x * 2) } else { None }
});
println!("{:?}", result); // Some(84)
// filter
let filtered = some.filter(|&x| x > 50);
println!("{:?}", filtered); // None (42 < 50)
// is_some / is_none
println!("some is some: {}", some.is_some()); // true
println!("none is none: {}", none.is_none()); // true
// unwrap: 直接取值,None 时 panic
println!("{}", some.unwrap()); // 42
// none.unwrap(); // panic!
// expect: 带消息的 unwrap
println!("{}", some.expect("应该有值"));
// none.expect("应该有值"); // panic: 应该有值
// zip: 组合两个 Option
let a = Some(1);
let b = Some("hello");
let zipped = a.zip(b);
println!("{:?}", zipped); // Some((1, "hello"))
}
Option 转换
fn main() {
// ok_or: Option<T> → Result<T, E>
let opt: Option<i32> = Some(42);
let res: Result<i32, &str> = opt.ok_or("值不存在");
println!("{:?}", res); // Ok(42)
// transpose: Option<Result<T, E>> → Result<Option<T>, E>
let x: Option<Result<i32, &str>> = Some(Ok(42));
let y: Result<Option<i32>, &str> = x.transpose();
println!("{:?}", y); // Ok(Some(42))
// flatten: Option<Option<T>> → Option<T>
let nested: Option<Option<i32>> = Some(Some(42));
let flat: Option<i32> = nested.flatten();
println!("{:?}", flat); // Some(42)
}
8.3 Result<T, E> 详解
use std::num::ParseIntError;
fn parse_double(s: &str) -> Result<i64, ParseIntError> {
let n = s.parse::<i64>()?;
Ok(n * 2)
}
fn main() {
// 成功
match parse_double("21") {
Ok(n) => println!("结果: {}", n), // 42
Err(e) => println!("错误: {}", e),
}
// 失败
match parse_double("abc") {
Ok(n) => println!("结果: {}", n),
Err(e) => println!("错误: {}", e), // invalid digit found in string
}
// 常用方法
let ok: Result<i32, &str> = Ok(42);
let err: Result<i32, &str> = Err("错误");
println!("{:?}", ok.unwrap_or(0)); // 42
println!("{:?}", err.unwrap_or(0)); // 0
println!("{:?}", ok.map(|x| x * 2)); // Ok(84)
println!("{:?}", err.map(|x| x * 2)); // Err("错误")
println!("{:?}", ok.ok()); // Some(42)
println!("{:?}", err.err()); // Some("错误")
}
8.4 模式匹配详解
match 表达式
fn main() {
let number = 13;
// 基本匹配
match number {
1 => println!("一"),
2 | 3 => println!("二或三"),
4..=10 => println!("四到十"),
_ => println!("其他: {}", number),
}
}
匹配守卫(Match Guard)
fn main() {
let pair = (2, -2);
match pair {
(x, y) if x == y => println!("相等"),
(x, y) if x + y == 0 => println!("互为相反数"),
(x, y) if x > 0 && y > 0 => println!("都在正区间"),
_ => println!("其他情况"),
}
}
解构结构体
struct Point {
x: i32,
y: i32,
}
fn main() {
let p = Point { x: 0, y: 7 };
match p {
Point { x, y: 0 } => println!("在x轴上: x={}", x),
Point { x: 0, y } => println!("在y轴上: y={}", y),
Point { x, y } => println!("不在轴上: ({}, {})", x, y),
}
}
解构枚举
enum Shape {
Circle(f64),
Rectangle { width: f64, height: f64 },
Triangle(f64, f64, f64),
}
fn describe(shape: &Shape) {
match shape {
Shape::Circle(r) => println!("圆形, 半径={}", r),
Shape::Rectangle { width, height } => {
println!("矩形, {}×{}", width, height);
}
Shape::Triangle(a, b, c) => {
println!("三角形, 边长={}, {}, {}", a, b, c);
}
}
}
fn main() {
let shapes = vec![
Shape::Circle(5.0),
Shape::Rectangle { width: 10.0, height: 5.0 },
Shape::Triangle(3.0, 4.0, 5.0),
];
for shape in &shapes {
describe(shape);
}
}
解构嵌套类型
enum Color {
Rgb(u8, u8, u8),
Rgba(u8, u8, u8, u8),
}
enum Background {
Solid(Color),
Transparent(Color, f32),
}
fn main() {
let bg = Background::Transparent(Color::Rgba(255, 0, 0, 128), 0.5);
match bg {
Background::Solid(Color::Rgb(r, g, b)) => {
println!("不透明: ({}, {}, {})", r, g, b);
}
Background::Transparent(Color::Rgba(r, g, b, a), opacity) => {
println!("透明: rgba({},{},{},{}) opacity={}", r, g, b, a, opacity);
}
_ => println!("其他"),
}
}
解构引用
fn main() {
let reference = &4;
match reference {
// &val 匹配引用并解引用
&val => println!("解引用: {}", val),
}
// 或者使用 ref 模式
match 4 {
ref val => println!("引用: {}", val),
}
// 解构元组中的引用
let pair = (&1, &2);
match pair {
(first, second) => println!("{} + {} = {}", first, second, first + second),
}
}
8.5 if let 与 while let
if let
当只关心一种情况时,比 match 更简洁:
fn main() {
let config_max: Option<u32> = Some(3);
// match 方式
match config_max {
Some(max) => println!("最大值: {}", max),
_ => (), // 必须处理 None
}
// if let 方式(更简洁)
if let Some(max) = config_max {
println!("最大值: {}", max);
}
// if let 带 else
let value: Option<i32> = None;
if let Some(v) = value {
println!("值: {}", v);
} else {
println!("没有值");
}
}
while let
循环直到模式不匹配:
fn main() {
let mut stack = vec![1, 2, 3, 4, 5];
// pop() 返回 Option<T>
while let Some(top) = stack.pop() {
println!("弹出: {}", top);
}
println!("栈已空: {:?}", stack);
}
matches! 宏
fn main() {
let x = 42;
// matches! 宏返回 bool
let is_positive = matches!(x, 1..=100);
println!("是正数(1-100): {}", is_positive);
// 在迭代器中使用
let nums = vec![Some(1), None, Some(3), None, Some(5)];
let some_count = nums.iter().filter(|n| matches!(n, Some(_))).count();
println!("Some 的数量: {}", some_count); // 3
}
8.6 业务场景示例
状态机
#[derive(Debug)]
enum TrafficLight {
Red { remaining_secs: u32 },
Yellow { remaining_secs: u32 },
Green { remaining_secs: u32 },
}
impl TrafficLight {
fn next(&self) -> TrafficLight {
match self {
TrafficLight::Red { .. } => TrafficLight::Green { remaining_secs: 60 },
TrafficLight::Green { .. } => TrafficLight::Yellow { remaining_secs: 5 },
TrafficLight::Yellow { .. } => TrafficLight::Red { remaining_secs: 90 },
}
}
fn action(&self) -> &str {
match self {
TrafficLight::Red { .. } => "停车",
TrafficLight::Yellow { .. } => "注意",
TrafficLight::Green { .. } => "通行",
}
}
fn remaining(&self) -> u32 {
match self {
TrafficLight::Red { remaining_secs }
| TrafficLight::Yellow { remaining_secs }
| TrafficLight::Green { remaining_secs } => *remaining_secs,
}
}
}
fn main() {
let mut light = TrafficLight::Red { remaining_secs: 90 };
for _ in 0..4 {
println!("{:?} → {} ({}秒)", light, light.action(), light.remaining());
light = light.next();
}
}
命令模式
#[derive(Debug)]
enum Command {
List,
Get { key: String },
Set { key: String, value: String },
Delete { key: String },
Quit,
}
impl Command {
fn parse(input: &str) -> Option<Command> {
let parts: Vec<&str> = input.trim().splitn(3, ' ').collect();
match parts.as_slice() {
["list"] => Some(Command::List),
["get", key] => Some(Command::Get { key: key.to_string() }),
["set", key, value] => Some(Command::Set {
key: key.to_string(),
value: value.to_string(),
}),
["delete", key] => Some(Command::Delete { key: key.to_string() }),
["quit"] | ["exit"] => Some(Command::Quit),
_ => None,
}
}
}
use std::collections::HashMap;
fn main() {
let mut store: HashMap<String, String> = HashMap::new();
// 模拟命令序列
let inputs = vec![
"set name Alice",
"set age 30",
"list",
"get name",
"delete age",
"list",
];
for input in inputs {
println!("> {}", input);
match Command::parse(input) {
Some(Command::List) => {
for (k, v) in &store {
println!(" {} = {}", k, v);
}
}
Some(Command::Get { key }) => {
match store.get(&key) {
Some(value) => println!(" {} = {}", key, value),
None => println!(" 未找到: {}", key),
}
}
Some(Command::Set { key, value }) => {
store.insert(key, value);
println!(" OK");
}
Some(Command::Delete { key }) => {
store.remove(&key);
println!(" 已删除");
}
Some(Command::Quit) => {
println!(" 再见!");
break;
}
None => println!(" 未知命令"),
}
}
}
JSON 值模拟
#[derive(Debug, Clone)]
enum JsonValue {
Null,
Bool(bool),
Number(f64),
Str(String),
Array(Vec<JsonValue>),
Object(Vec<(String, JsonValue)>),
}
impl JsonValue {
fn to_json(&self) -> String {
match self {
JsonValue::Null => "null".to_string(),
JsonValue::Bool(b) => b.to_string(),
JsonValue::Number(n) => format!("{}", n),
JsonValue::Str(s) => format!("\"{}\"", s),
JsonValue::Array(arr) => {
let items: Vec<String> = arr.iter().map(|v| v.to_json()).collect();
format!("[{}]", items.join(", "))
}
JsonValue::Object(pairs) => {
let items: Vec<String> = pairs
.iter()
.map(|(k, v)| format!("\"{}\": {}", k, v.to_json()))
.collect();
format!("{{{}}}", items.join(", "))
}
}
}
fn is_truthy(&self) -> bool {
match self {
JsonValue::Null => false,
JsonValue::Bool(b) => *b,
JsonValue::Number(n) => *n != 0.0,
JsonValue::Str(s) => !s.is_empty(),
JsonValue::Array(a) => !a.is_empty(),
JsonValue::Object(o) => !o.is_empty(),
}
}
}
fn main() {
let data = JsonValue::Object(vec![
("name".into(), JsonValue::Str("Alice".into())),
("age".into(), JsonValue::Number(30.0)),
("active".into(), JsonValue::Bool(true)),
("address".into(), JsonValue::Null),
("scores".into(), JsonValue::Array(vec![
JsonValue::Number(95.0),
JsonValue::Number(87.0),
JsonValue::Number(92.0),
])),
]);
println!("{}", data.to_json());
}
8.7 本章小结
| 要点 | 说明 |
|---|---|
| 枚举 | 使用 enum 定义,变体可以携带不同类型的数据 |
| Option | Some(T) 或 None,替代 null |
| Result | Ok(T) 或 Err(E),表示可能失败的操作 |
| match | 穷尽匹配,必须覆盖所有变体 |
| if let | 只关心一种情况时使用 |
| while let | 循环直到模式不匹配 |
| matches! | 返回 bool 的模式匹配宏 |
| 解构 | 可以解构结构体、枚举、元组、引用等 |
扩展阅读
- Rust Book - 枚举 — 官方教程
- Rust Book - 模式匹配 — 模式匹配详解
- Option 文档 — Option API 参考