Day 8: 枚举与模式匹配
今天是《30天精通Rust语言》系列的第八天。我们将学习枚举(Enum)和模式匹配(Pattern Matching)。枚举允许你定义一个类型其值属于有限的集合之一,而模式匹配则提供了强大的控制流结构来处理这些值。
2. 枚举基础
Section titled “2. 枚举基础”2.1 基本枚举定义
Section titled “2.1 基本枚举定义”enum Direction { Up, Down, Left, Right,}
fn main() { let direction = Direction::Up;
match direction { Direction::Up => println!("Moving up"), Direction::Down => println!("Moving down"), Direction::Left => println!("Moving left"), Direction::Right => println!("Moving right"), }}2.2 带数据的枚举
Section titled “2.2 带数据的枚举”枚举的每个变体可以携带不同类型和数量的数据:
enum Message { Quit, Move { x: i32, y: i32 }, Write(String), ChangeColor(i32, i32, i32),}
fn main() { let msg1 = Message::Quit; let msg2 = Message::Move { x: 10, y: 20 }; let msg3 = Message::Write(String::from("Hello")); let msg4 = Message::ChangeColor(255, 0, 0);}3. match模式匹配
Section titled “3. match模式匹配”match 是Rust最强大的控制流结构,允许你将一个值与多个模式进行比较并执行相应代码。
3.1 基本match
Section titled “3.1 基本match”enum Coin { Penny, Nickel, Dime, Quarter,}
fn value_in_cents(coin: Coin) -> u32 { match coin { Coin::Penny => 1, Coin::Nickel => 5, Coin::Dime => 10, Coin::Quarter => 25, }}
fn main() { println!("Penny = {} cents", value_in_cents(Coin::Penny)); println!("Quarter = {} cents", value_in_cents(Coin::Quarter));}3.2 绑定值的模式
Section titled “3.2 绑定值的模式”可以从枚举变体中提取数据到变量:
enum Message { Move { x: i32, y: i32 },}
fn main() { let msg = Message::Move { x: 5, y: 10 };
match msg { Message::Move { x, y } => println!("Move to ({}, {})", x, y), _ => println!("Other message"), }}4. Option 枚举
Section titled “4. Option 枚举”Option<T> 是Rust标准库中最重要的枚举之一,用于处理值可能不存在的情况。
4.1 Option的定义
Section titled “4.1 Option的定义”enum Option<T> { Some(T), // 有值 None, // 无值}4.2 使用Option
Section titled “4.2 使用Option”fn main() { let some_number: Option<i32> = Some(5); let some_string: Option<&str> = Some("a string"); let absent_number: Option<i32> = None;
if let Some(value) = some_number { println!("Got value: {}", value); }
match some_number { Some(n) => println!("Got number: {}", n), None => println!("No number"), }}4.3 Option的处理方法
Section titled “4.3 Option的处理方法”fn main() { let x: Option<i32> = Some(5);
// 使用match处理 let y = match x { Some(v) => v * 2, None => 0, }; println!("y = {}", y);
// 使用unwrap_or提供默认值 let x = Some(10); let y = x.unwrap_or(0); println!("y = {}", y);
// 使用unwrap_or_else懒加载默认值 let x: Option<i32> = None; let y = x.unwrap_or_else(|| { println!("Computing default..."); 42 }); println!("y = {}", y);}5. if let简化
Section titled “5. if let简化”当只关心一种模式时,if let 提供了更简洁的写法:
fn main() { let some_value: Option<i32> = Some(3);
// 传统match写法 match some_value { Some(3) => println!("three"), _ => println!("other"), }
// if let简化 if let Some(3) = some_value { println!("three"); } else { println!("other"); }}6. 穷尽匹配
Section titled “6. 穷尽匹配”match 要求处理所有可能的模式,使用 _ 处理不需要关心的其他情况:
fn main() { let some_value: Option<i32> = Some(42);
match some_value { Some(0) => println!("zero"), Some(n) => println!("non-zero: {}", n), None => println!("nothing"), }}如果省略任何情况,编译器会报错,确保你不会遗漏任何可能的值。
7. if let else
Section titled “7. if let else”if let 也可以配合 else 使用:
fn main() { let color: Option<&str> = Some("red");
if let Some(c) = color { println!("Color is {}", c); } else { println!("No color"); }}8. while let循环
Section titled “8. while let循环”while let 在条件为真时持续循环:
fn main() { let mut stack = Vec::new(); stack.push(1); stack.push(2); stack.push(3);
while let Some(top) = stack.pop() { println!("Popped: {}", top); }}9. 实用示例
Section titled “9. 实用示例”让我们创建一个综合示例,展示枚举和模式匹配的威力:
enum Shape { Circle(f64), // 半径 Rectangle(f64, f64), // 宽和高 Triangle(f64, f64, f64), // 三边}
fn area(shape: &Shape) -> f64 { match shape { Shape::Circle(r) => std::f64::consts::PI * r * r, Shape::Rectangle(w, h) => w * h, Shape::Triangle(a, b, c) => { let s = (a + b + c) / 2.0; (s * (s - a) * (s - b) * (s - c)).sqrt() } }}
fn main() { let shapes = vec![ Shape::Circle(5.0), Shape::Rectangle(4.0, 6.0), Shape::Triangle(3.0, 4.0, 5.0), ];
for shape in &shapes { println!("Area: {:.2}", area(shape)); }}10. 实践练习
Section titled “10. 实践练习”今天的实践任务:
- 定义一个
Season枚举,包含春、夏、秋、冬四个变体。 - 为
Season实现一个方法,返回该季节的典型天气描述。 - 使用
match编写函数,根据Option<i32>返回值是Some还是None打印不同信息。 - 使用
if let简化处理Result类型的代码(提前尝鲜,Result在下一章详细讲解)。 - 实现一个简单的计算器,使用枚举表示加、减、乘、除操作,使用
match计算结果。
11. 常见问题
Section titled “11. 常见问题”问:为什么Rust要引入 Option 而不是使用 null?
答:Rust的 Option<T> 是类型系统的一部分,编译器强制你处理值不存在的情况。而 null 是值语言之外的特殊值,容易被忽略导致运行时错误。Rust的设计让你在编译时就处理所有可能的空值情况。
问:什么时候用 if let 而不是 match?
答:当只需要处理一种模式,而忽略其他所有情况时,使用 if let 更简洁。如果需要处理多种模式,或需要绑定值,match 更适合。
12. 总结
Section titled “12. 总结”今天我们学习了枚举与模式匹配:
- 枚举允许定义有限集合的类型,变体可以携带数据。
match提供穷尽式模式匹配,编译器确保所有情况被处理。Option<T>是处理可能不存在值的标准方式。if let和while let提供了更简洁的单模式匹配语法。
明天我们将学习Rust的错误处理机制:Result<T,E>、? 运算符和 panic!。加油!