Skip to content

Day 8: 枚举与模式匹配

今天是《30天精通Rust语言》系列的第八天。我们将学习枚举(Enum)和模式匹配(Pattern Matching)。枚举允许你定义一个类型其值属于有限的集合之一,而模式匹配则提供了强大的控制流结构来处理这些值。

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"),
}
}

枚举的每个变体可以携带不同类型和数量的数据:

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);
}

match 是Rust最强大的控制流结构,允许你将一个值与多个模式进行比较并执行相应代码。

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));
}

可以从枚举变体中提取数据到变量:

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"),
}
}

Option<T> 是Rust标准库中最重要的枚举之一,用于处理值可能不存在的情况。

enum Option<T> {
Some(T), // 有值
None, // 无值
}
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"),
}
}
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);
}

当只关心一种模式时,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");
}
}

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"),
}
}

如果省略任何情况,编译器会报错,确保你不会遗漏任何可能的值。

if let 也可以配合 else 使用:

fn main() {
let color: Option<&str> = Some("red");
if let Some(c) = color {
println!("Color is {}", c);
} else {
println!("No color");
}
}

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);
}
}

让我们创建一个综合示例,展示枚举和模式匹配的威力:

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));
}
}

今天的实践任务:

  1. 定义一个 Season 枚举,包含春、夏、秋、冬四个变体。
  2. 为 Season 实现一个方法,返回该季节的典型天气描述。
  3. 使用 match 编写函数,根据 Option<i32> 返回值是Some还是None打印不同信息。
  4. 使用 if let 简化处理 Result 类型的代码(提前尝鲜,Result在下一章详细讲解)。
  5. 实现一个简单的计算器,使用枚举表示加、减、乘、除操作,使用 match 计算结果。

问:为什么Rust要引入 Option 而不是使用 null?

答:Rust的 Option<T> 是类型系统的一部分,编译器强制你处理值不存在的情况。而 null 是值语言之外的特殊值,容易被忽略导致运行时错误。Rust的设计让你在编译时就处理所有可能的空值情况。

问:什么时候用 if let 而不是 match?

答:当只需要处理一种模式,而忽略其他所有情况时,使用 if let 更简洁。如果需要处理多种模式,或需要绑定值,match 更适合。

今天我们学习了枚举与模式匹配:

  • 枚举允许定义有限集合的类型,变体可以携带数据。
  • match 提供穷尽式模式匹配,编译器确保所有情况被处理。
  • Option<T> 是处理可能不存在值的标准方式。
  • if let 和 while let 提供了更简洁的单模式匹配语法。

明天我们将学习Rust的错误处理机制:Result<T,E>、? 运算符和 panic!。加油!