跳到主要内容

模式匹配(Pattern Matching)

模式匹配(Pattern Matching)是 Rust 中一个强大的控制流结构,允许你将值与一系列模式进行比较,并根据匹配的模式执行代码。

基本 match 语法

fn main() {
let x = 1;

match x {
1 => println!("一"),
2 => println!("二"),
3 => println!("三"),
_ => println!("其他"),
}
}

匹配字面值

fn main() {
let x = 1;

match x {
1 => println!("一"),
2 => println!("二"),
3 => println!("三"),
_ => println!("其他任何值"),
}
}

匹配命名变量

fn main() {
let x = Some(5);
let y = 10;

match x {
Some(50) => println!("匹配到 50"),
Some(y) => println!("匹配到 {y}"), // 这里的 y 是新变量,遮蔽了外部的 y
_ => println!("默认情况, x = {:?}", x),
}

println!("最后: x = {:?}, y = {y}", x);
}

多个模式

使用 | 语法匹配多个模式:

fn main() {
let x = 1;

match x {
1 | 2 => println!("一或二"),
3 => println!("三"),
_ => println!("其他"),
}
}

通过范围匹配值

fn main() {
let x = 5;

match x {
1..=5 => println!("一到五"),
_ => println!("其他"),
}

let x = 'c';

match x {
'a'..='j' => println!("ASCII 字母的前半部分"),
'k'..='z' => println!("ASCII 字母的后半部分"),
_ => println!("其他字符"),
}
}

解构结构体

struct Point {
x: i32,
y: i32,
}

fn main() {
let p = Point { x: 0, y: 7 };

match p {
Point { x, y: 0 } => println!("在 x 轴上的点 ({}, 0)", x),
Point { x: 0, y } => println!("在 y 轴上的点 (0, {})", y),
Point { x, y } => println!("其他点 ({}, {})", x, y),
}
}

解构结构体的简写

struct Point {
x: i32,
y: i32,
}

fn main() {
let p = Point { x: 0, y: 7 };

let Point { x, y } = p;
assert_eq!(0, x);
assert_eq!(7, y);
}

解构枚举

enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}

fn main() {
let msg = Message::ChangeColor(0, 160, 255);

match msg {
Message::Quit => {
println!("退出消息");
}
Message::Move { x, y } => {
println!("移动到坐标 x: {x}, y: {y}");
}
Message::Write(text) => {
println!("文本消息: {}", text);
}
Message::ChangeColor(r, g, b) => {
println!("改变颜色为红: {r}, 绿: {g}, 蓝: {b}");
}
}
}

解构嵌套的结构体和枚举

enum Color {
Rgb(i32, i32, i32),
Hsv(i32, i32, i32),
}

enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(Color),
}

fn main() {
let msg = Message::ChangeColor(Color::Hsv(0, 160, 255));

match msg {
Message::ChangeColor(Color::Rgb(r, g, b)) => {
println!("改变颜色为红: {r}, 绿: {g}, 蓝: {b}");
}
Message::ChangeColor(Color::Hsv(h, s, v)) => {
println!("改变颜色为色调: {h}, 饱和度: {s}, 明度: {v}");
}
_ => (),
}
}

解构数组和切片

fn main() {
let arr = [1, 2, 3];

match arr {
[1, 2, 3] => println!("匹配 [1, 2, 3]"),
[1, 2, _] => println!("以 1, 2 开头"),
[1, ..] => println!("以 1 开头"),
_ => println!("其他"),
}

// 解构切片
let slice = &[1, 2, 3, 4, 5];

match slice {
[first, .., last] => println!("第一个: {}, 最后一个: {}", first, last),
_ => println!("其他"),
}
}

忽略模式中的值

使用 _ 忽略整个值

fn foo(_: i32, y: i32) {
println!("这个函数只使用 y 参数: {}", y);
}

fn main() {
foo(3, 4);
}

使用嵌套的 _ 忽略部分值

fn main() {
let mut setting_value = Some(5);
let new_setting_value = Some(10);

match (setting_value, new_setting_value) {
(Some(_), Some(_)) => {
println!("不能覆盖现有的自定义值");
}
_ => {
setting_value = new_setting_value;
}
}

println!("设置是 {:?}", setting_value);
}

使用 .. 忽略剩余部分

struct Point {
x: i32,
y: i32,
z: i32,
}

fn main() {
let origin = Point { x: 0, y: 0, z: 0 };

match origin {
Point { x, .. } => println!("x 是 {}", x),
}
}

匹配守卫

匹配守卫(match guard)是一个指定于 match 分支模式之后的额外 if 条件:

fn main() {
let num = Some(4);

match num {
Some(x) if x < 5 => println!("小于五: {}", x),
Some(x) => println!("{}", x),
None => (),
}
}

在匹配守卫中使用外部变量

fn main() {
let x = Some(5);
let y = 10;

match x {
Some(50) => println!("匹配到 50"),
Some(n) if n == y => println!("匹配到 {n}"),
_ => println!("默认情况, x = {:?}", x),
}

println!("最后: x = {:?}, y = {y}", x);
}

@ 绑定

@ 运算符允许我们在创建一个存放值的变量的同时测试其值是否匹配模式:

enum Message {
Hello { id: i32 },
}

fn main() {
let msg = Message::Hello { id: 5 };

match msg {
Message::Hello {
id: id_variable @ 3..=7,
} => println!("找到了一个在范围内的 id: {}", id_variable),
Message::Hello { id: 10..=12 } => {
println!("找到了另一个在范围内的 id");
}
Message::Hello { id } => println!("找到了其他 id: {}", id),
}
}

if let 和 while let

if let

当你只关心一个特定的匹配情况时:

fn main() {
let favorite_color: Option<&str> = None;
let is_tuesday = false;
let age: Result<u8, _> = "34".parse();

if let Some(color) = favorite_color {
println!("使用你最喜欢的颜色 {} 作为背景", color);
} else if is_tuesday {
println!("星期二是绿色的日子!");
} else if let Ok(age) = age {
if age > 30 {
println!("使用紫色作为背景颜色");
} else {
println!("使用橙色作为背景颜色");
}
} else {
println!("使用蓝色作为背景颜色");
}
}

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!("{}", top);
}
}

最佳实践

  1. 使用 _ 忽略不需要的值:避免编译器警告
  2. 合理使用匹配守卫:增加模式的表达能力
  3. 优先使用 if let 处理单一情况:代码更简洁
  4. 确保模式覆盖所有情况:避免运行时 panic
// 好的模式匹配示例
fn process_option(opt: Option<i32>) -> String {
match opt {
Some(n) if n > 0 => format!("正数: {}", n),
Some(n) if n < 0 => format!("负数: {}", n),
Some(0) => "零".to_string(),
None => "无值".to_string(),
}
}

fn main() {
println!("{}", process_option(Some(42)));
println!("{}", process_option(Some(-10)));
println!("{}", process_option(Some(0)));
println!("{}", process_option(None));
}