Rust: Rust 模式匹配进阶:守卫、@绑定与多模式
最后更新:2026-08-26
模式匹配是 Rust 最强大的语言特性之一,而
match守卫、@绑定、多模式等进阶技巧让你在匹配时能做更多精细控制——不是简单的"匹配值",而是"匹配条件"。
如果说基础 match 是选择题(选一个分支执行),那进阶模式匹配就是综合题——你可以同时检查值、绑定引用、附加条件判断。
1. 你将学到
- 使用
if守卫(match guard)给 match 分支加额外条件 - 使用
@绑定在匹配时同时绑定值 - 使用
|组合多个模式(多模式匹配) - 使用
..和..=进行范围匹配 - 使用
matches!宏进行简洁的布尔匹配判断 - 解构结构体和枚举的嵌套模式
2. 一个员工绩效评级的故事
(1) 痛苦:用 if-else 链做综合评级
Tom 是公司的 HR 经理,他需要根据员工的绩效分数和考勤天数做综合评级。
规则如下:
| 等级 | 绩效分 | 考勤要求 |
|---|---|---|
| A+ | ≥95 | 全勤(≥22天) |
| A | ≥85 | 考勤≥20天 |
| B | ≥70 | 考勤≥18天 |
| C | ≥60 | — |
| D | <60 | — |
最开始他写了一大串 if-else:
fn rate_employee(score: u32, attendance: u32) -> &'static str {
if score >= 95 && attendance >= 22 {
"A+"
} else if score >= 85 && attendance >= 20 {
"A"
} else if score >= 70 && attendance >= 18 {
"B"
} else if score >= 60 {
"C"
} else {
"D"
}
}
这段代码虽然能跑,但问题不少:条件散落在 if-else 链中,可读性差,且没有穷尽性检查——如果未来增加评级规则,很容易漏掉。而且分数和考勤的耦合关系不够清晰。
(2) Rust 模式匹配进阶的方案
fn rate_employee(score: u32, attendance: u32) -> &'static str {
match score {
// 守卫条件:分数达标 + 考勤达标
s if s >= 95 && attendance >= 22 => "A+",
s if s >= 85 && attendance >= 20 => "A",
s if s >= 70 && attendance >= 18 => "B",
s if s >= 60 => "C",
_ => "D", // 兜底:分数 < 60
}
}
fn main() {
let employees = [
("Alice", 98, 23),
("Bob", 88, 21),
("Charlie", 88, 17), // 分数高但考勤不够
("Diana", 72, 19),
("Eve", 55, 20),
];
for (name, score, attendance) in &employees {
let rating = rate_employee(*score, *attendance);
println!("{:8} | 分数: {:2} | 考勤: {:2}天 | 评级: {}",
name, score, attendance, rating);
}
}
输出:
Alice | 分数: 98 | 考勤: 23天 | 评级: A+
Bob | 分数: 88 | 考勤: 21天 | 评级: A
Charlie | 分数: 88 | 考勤: 17天 | 评级: B
Diana | 分数: 72 | 考勤: 19天 | 评级: B
Eve | 分数: 55 | 考勤: 20天 | 评级: D
match守卫让每个分支的条件一目了然:分数 + 考勤的"与"关系清晰可见。_兜底分支保证了穷尽性。而且不同分数区间用s if s >= ...的语义比else if更直观。
3. 进阶模式匹配概览
(1) 概念图
graph TB
A[模式匹配进阶] --> B[match 守卫]
A --> C[@ 绑定]
A --> D[多模式 |]
A --> E[范围匹配]
A --> F[matches! 宏]
A --> G[解构模式]
B --> B1["match x { n if n > 10 => ... }"]
C --> C1["match x { e @ 1..=5 => ... }"]
D --> D1["match x { 1 | 3 | 5 => ... }"]
E --> E1["match x { 1..=10 => ... }"]
F --> F1["if matches!(x, 1..=5) { ... }"]
G --> G1["let Point { x, y } = p;"]
G --> G2["match opt { Some(v @ 1..=10) => ... }"]
(2) 匹配方式对比
| 方式 | 语法 | 何时使用 | 示例 |
|---|---|---|---|
| 基础 match | match x { PAT => .. } |
简单值匹配 | match x { 1 => "one" } |
| match 守卫 | match x { PAT if COND => .. } |
匹配 + 额外条件 | n if n > 10 && n % 2 == 0 |
| @ 绑定 | e @ PAT => .. |
匹配时绑定值 | e @ 1..=10 => e |
| 多模式 | | PAT1 | PAT2 => .. |
多个模式共享同一分支 | 1 | 3 | 5 => "odd" |
| 范围 ..= | PAT1..=PAT2 => .. |
匹配一个值范围 | 1..=5 => "small" |
| matches! 宏 | matches!(x, PAT) |
只需布尔结果 | if matches!(x, 1..=5) |
| 解构 | let PAT = value |
分解复合类型 | let (a, b) = pair |
(3) 模式类型速查
| 模式类型 | 语法 | 匹配目标 | 示例 |
|---|---|---|---|
| 字面量 | 1 / "hello" |
精确值 | match x { 1 => ... } |
| 变量绑定 | x |
任意值,绑定到 x | match x { n => ... } |
| 通配符 | _ |
任意值,忽略 | match x { _ => ... } |
| 多模式 | 1 | 2 | 3 |
多个值 | match x { 1 | 2 => ... } |
| 范围 | 1..=5 |
闭区间范围 | match x { 1..=5 => ... } |
| 解构元组 | (a, b) |
元组 | match pair { (x, y) => ... } |
| 解构结构体 | Point { x, y } |
结构体 | match p { Point { x, y } => ... } |
| 解构枚举 | Some(v) |
枚举变体 | match opt { Some(v) => ... } |
| @ 绑定 | e @ 1..=10 |
范围 + 绑定 | match x { e @ 1..=10 => ... } |
| 守卫 | x if x > 0 |
模式 + 条件 | match x { n if n > 0 => ... } |
| 忽略剩余 | .. |
忽略部分字段 | match p { Point { x, .. } => ... } |
4. 进阶匹配示例
▶ 示例 1:match 守卫 + @ 绑定 + 多模式(难度 ⭐⭐)
// ============================================
// 综合示例:员工绩效评级系统
// 展示 match 守卫、@ 绑定、多模式
// ============================================
#[derive(Debug)]
enum Department {
Engineering,
Sales,
HR,
Management,
}
#[derive(Debug)]
struct Employee {
name: String,
score: u32,
attendance: u32,
department: Department,
}
impl Employee {
/// Rate employee using advanced pattern matching.
/// Returns (rating, bonus_percentage).
fn rate(&self) -> (&'static str, u32) {
match (self.score, self.attendance) {
// @ binding: capture the matched score value
s @ 95..=100 if self.attendance >= 22 => {
println!(" [@ binding] score {} captured for A+ rating", s);
("A+", 30)
}
s @ 85..=94 if self.attendance >= 20 => {
println!(" [@ binding] score {} captured for A rating", s);
("A", 20)
}
s @ 70..=84 if self.attendance >= 18 => {
println!(" [@ binding] score {} captured for B rating", s);
("B", 10)
}
// Multi-pattern: 60..=69 OR exactly 70 with low attendance
60..=69 | 70..=84 => {
("C", 5) // No @ binding needed here
}
// match guard with combined conditions
s if s < 60 => {
println!(" [guard] score {} is below 60, rating D", s);
("D", 0)
}
// Catch-all: should not reach normally
_ => {
println!(" [wildcard] unexpected combination");
("Unknown", 0)
}
}
}
/// Check if this employee qualifies for special bonus
/// using matches! macro.
fn has_special_bonus(&self) -> bool {
// matches! returns true if the pattern matches
matches!(self.department, Department::Engineering | Department::Management)
&& self.score >= 90
}
}
fn main() {
let employees = vec![
Employee {
name: String::from("Alice"),
score: 97,
attendance: 23,
department: Department::Engineering,
},
Employee {
name: String::from("Bob"),
score: 88,
attendance: 21,
department: Department::Sales,
},
Employee {
name: String::from("Charlie"),
score: 88,
attendance: 17,
department: Department::HR,
},
Employee {
name: String::from("Diana"),
score: 65,
attendance: 20,
department: Department::HR,
},
Employee {
name: String::from("Eve"),
score: 42,
attendance: 15,
department: Department::Management,
},
];
for emp in &employees {
println!("{}:", emp.name);
let (rating, bonus) = emp.rate();
let special = emp.has_special_bonus();
println!(" Rating: {}, Bonus: {}%, Special: {}",
rating, bonus, special);
println!();
}
}
输出:
Alice:
[@ binding] score 97 captured for A+ rating
Rating: A+, Bonus: 30%, Special: true
Bob:
[@ binding] score 88 captured for A rating
Rating: A, Bonus: 20%, Special: false
Charlie:
[@ binding] score 88 captured for B rating
Rating: B, Bonus: 10%, Special: false
Diana:
Rating: C, Bonus: 5%, Special: false
Eve:
[guard] score 42 is below 60, rating D
Rating: D, Bonus: 0%, Special: true
这个示例综合展示了三种进阶匹配技巧:
@绑定捕获匹配到的值供后续使用,if守卫添加额外条件(如考勤要求),|多模式让不同区间共享同一分支。matches!宏则简洁地判断"是否属于某几个变体"。
▶ 示例 2:解构结构体和枚举的嵌套模式(难度 ⭐⭐⭐)
// ============================================
// 嵌套解构:匹配结构体中的枚举,枚举中的元组
// ============================================
#[derive(Debug)]
enum TaskStatus {
Pending,
InProgress,
Completed { finished_at: String, reviewer: String },
Blocked { reason: String, blocked_by: String },
}
#[derive(Debug)]
struct Task {
id: u32,
title: String,
status: TaskStatus,
priority: u8, // 1 (highest) to 5 (lowest)
}
fn analyze_task(task: &Task) {
// Destructure the Task struct directly in match
match task {
// Multi-pattern: priority 1 or 2 with any status
Task { priority: 1..=2, .. } => {
println!(" [range] High priority task (level {})", task.priority);
}
// Destructure both Task and TaskStatus::Completed
Task {
id,
title,
status:
TaskStatus::Completed {
finished_at,
reviewer,
},
priority,
} => {
println!(" [nested destructure] Task #{} '{}' completed by {} at {} (priority {})",
id, title, reviewer, finished_at, priority);
}
// Destructure TaskStatus::Blocked with @ binding on reason
Task {
status:
TaskStatus::Blocked {
reason,
blocked_by,
},
..
} => {
println!(" [@ binding] Task '{}' blocked by {}: {}",
task.title, blocked_by, reason);
}
// Match guard on enum variant
Task {
status: TaskStatus::InProgress,
priority,
..
} if *priority <= 3 => {
println!(" [guard] Important in-progress task (priority {})", priority);
}
// Wildcard for remaining
_ => {
println!(" [wildcard] Task '{}': status {:?}", task.title, task.status);
}
}
}
fn main() {
let tasks = vec![
Task {
id: 1,
title: String::from("Security audit"),
status: TaskStatus::Completed {
finished_at: String::from("2026-07-01"),
reviewer: String::from("Alice"),
},
priority: 1,
},
Task {
id: 2,
title: String::from("Update dependencies"),
status: TaskStatus::Blocked {
reason: String::from("Waiting for approval"),
blocked_by: String::from("Manager"),
},
priority: 2,
},
Task {
id: 3,
title: String::from("Write documentation"),
status: TaskStatus::InProgress,
priority: 3,
},
Task {
id: 4,
title: String::from("Fix typo in README"),
status: TaskStatus::Pending,
priority: 5,
},
];
for task in &tasks {
println!("Task #{}: {}", task.id, task.title);
analyze_task(task);
println!();
}
}
输出:
Task #1: Security audit
[nested destructure] Task #1 'Security audit' completed by Alice at 2026-07-01 (priority 1)
Task #2: Update dependencies
[range] High priority task (level 2)
Task #3: Write documentation
[guard] Important in-progress task (priority 3)
Task #4: Fix typo in README
[wildcard] Task 'Fix typo in README': status Pending
嵌套解构是 Rust 模式匹配的强大能力:你可以在一个
match分支中同时解构结构体、枚举、元组。..忽略不关心的字段,@绑定捕获深层值。注意Task { priority: 1..=2, .. }匹配了 priority 为 1 或 2 的任何状态的任务——因为Blocked的匹配在后面,但range模式先匹配了 priority=2 的任务。
▶ 示例 3:matches! 宏与 if let 组合(难度 ⭐⭐)
// ============================================
// matches! 宏:简洁的布尔模式匹配
// ============================================
#[derive(Debug, PartialEq)]
enum HttpStatus {
Ok,
NotFound,
ServerError(u16),
Redirect(u16),
}
/// Check if a status is a success (2xx).
fn is_success(status: &HttpStatus) -> bool {
matches!(status, HttpStatus::Ok)
}
/// Check if a status is a server error (5xx).
fn is_server_error(status: &HttpStatus) -> bool {
matches!(status, HttpStatus::ServerError(_))
}
/// Check if a status is a redirect (3xx) with specific code.
fn is_redirect_to(status: &HttpStatus, code: u16) -> bool {
matches!(status, HttpStatus::Redirect(c) if *c == code)
}
/// Get status category using multiple matches! checks.
fn categorize(status: &HttpStatus) -> &'static str {
if matches!(status, HttpStatus::Ok) {
"Success"
} else if matches!(status, HttpStatus::Redirect(301 | 302)) {
"Temporary Redirect"
} else if matches!(status, HttpStatus::Redirect(_)) {
"Other Redirect"
} else if matches!(status, HttpStatus::NotFound) {
"Not Found (404)"
} else if matches!(status, HttpStatus::ServerError(500..=599)) {
"Server Error"
} else {
"Unknown"
}
}
fn main() {
let statuses = vec![
HttpStatus::Ok,
HttpStatus::NotFound,
HttpStatus::Redirect(301),
HttpStatus::Redirect(307),
HttpStatus::ServerError(500),
HttpStatus::ServerError(503),
];
for status in &statuses {
println!("{:?}:", status);
println!(" is_success: {}", is_success(status));
println!(" is_server_error: {}", is_server_error(status));
println!(" is_redirect_to_301: {}", is_redirect_to(status, 301));
println!(" category: {}", categorize(status));
// if let with matches!-style pattern
if let HttpStatus::ServerError(code) = status {
println!(" >> Server error code: {}", code);
}
println!();
}
}
输出:
Ok:
is_success: true
is_server_error: false
is_redirect_to_301: false
category: Success
NotFound:
is_success: false
is_server_error: false
is_redirect_to_301: false
category: Not Found (404)
Redirect(301):
is_success: false
is_server_error: false
is_redirect_to_301: true
category: Temporary Redirect
Redirect(307):
is_success: false
is_server_error: false
is_redirect_to_301: false
category: Other Redirect
ServerError(500):
is_success: false
is_server_error: true
is_redirect_to_301: false
category: Server Error
ServerError(503):
is_success: false
is_server_error: true
is_redirect_to_301: false
category: Server Error
matches!宏返回一个bool,适合在if条件中做模式匹配。它支持if守卫、多模式、范围匹配等所有模式语法。与if let不同,matches!不绑定变量,只返回true/false——当你只需要判断而不需要取值时用它最简洁。
▶ 示例 4:综合练习——表达式求值器(难度 ⭐⭐⭐)
// ============================================
// 综合示例:嵌套模式匹配实现表达式求值
// ============================================
#[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>),
}
fn eval(expr: &Expr) -> Option<f64> {
match expr {
Expr::Number(n) => Some(*n),
Expr::Add(a, b) => Some(eval(a)? + eval(b)?),
Expr::Sub(a, b) => Some(eval(a)? - eval(b)?),
Expr::Mul(a, b) => Some(eval(a)? * eval(b)?),
Expr::Div(a, b) => {
let divisor = eval(b)?;
if divisor == 0.0 { None } else { Some(eval(a)? / divisor) }
}
Expr::Neg(a) => Some(-eval(a)?),
}
}
fn expr_to_string(expr: &Expr) -> String {
match expr {
Expr::Number(n) => format!("{:.0}", n),
Expr::Add(a, b) => format!("({} + {})", expr_to_string(a), expr_to_string(b)),
Expr::Sub(a, b) => format!("({} - {})", expr_to_string(a), expr_to_string(b)),
Expr::Mul(a, b) => format!("({} * {})", expr_to_string(a), expr_to_string(b)),
Expr::Div(a, b) => format!("({} / {})", expr_to_string(a), expr_to_string(b)),
Expr::Neg(a) => format!("-{}", expr_to_string(a)),
}
}
fn main() {
let expr1 = Expr::Add(
Box::new(Expr::Number(10.0)),
Box::new(Expr::Mul(Box::new(Expr::Number(3.0)), Box::new(Expr::Number(4.0)))),
);
let expr2 = Expr::Div(
Box::new(Expr::Sub(Box::new(Expr::Number(20.0)), Box::new(Expr::Number(5.0)))),
Box::new(Expr::Number(3.0)),
);
let expr3 = Expr::Neg(Box::new(Expr::Number(42.0)));
let expr4 = Expr::Div(Box::new(Expr::Number(10.0)), Box::new(Expr::Number(0.0)));
let exprs = [expr1, expr2, expr3, expr4];
for (i, expr) in exprs.iter().enumerate() {
let s = expr_to_string(expr);
match eval(expr) {
Some(val) => println!("表达式{}: {} = {:.2}", i + 1, s, val),
None => println!("表达式{}: {} = 错误(除以零)", i + 1, s),
}
}
}
输出:
表达式1: (10 + (3 * 4)) = 22.00
表达式2: ((20 - 5) / 3) = 5.00
表达式3: -42 = -42.00
表达式4: (10 / 0) = 错误(除以零)
递归枚举 + 嵌套解构是模式匹配最强大的应用之一。
Box<Expr>让枚举可以引用自身(因为 Box 是固定大小的指针)。eval用?操作符优雅地传播除零错误。
▶ 示例 5:模式匹配与 JSON-like 数据结构(难度 ⭐⭐⭐)
// ============================================
// 用模式匹配处理动态类型数据
// ============================================
#[derive(Debug, Clone)]
enum Value {
Null,
Bool(bool),
Number(f64),
String(String),
Array(Vec<Value>),
Object(Vec<(String, Value)>),
}
fn type_name(val: &Value) -> &str {
match val {
Value::Null => "null",
Value::Bool(_) => "bool",
Value::Number(_) => "number",
Value::String(_) => "string",
Value::Array(_) => "array",
Value::Object(_) => "object",
}
}
fn deep_count(val: &Value) -> usize {
match val {
Value::Array(items) => items.iter().map(deep_count).sum::<usize>() + 1,
Value::Object(entries) => entries.iter().map(|(_, v)| deep_count(v)).sum::<usize>() + 1,
_ => 1,
}
}
fn find_key<'a>(val: &'a Value, key: &str) -> Option<&'a Value> {
match val {
Value::Object(entries) => entries.iter().find(|(k, _)| k == key).map(|(_, v)| v),
_ => None,
}
}
fn main() {
let data = Value::Object(vec![
("name".into(), Value::String("Alice".into())),
("age".into(), Value::Number(30.0)),
("active".into(), Value::Bool(true)),
("scores".into(), Value::Array(vec![
Value::Number(95.0),
Value::Number(88.0),
Value::Number(92.0),
])),
("address".into(), Value::Object(vec![
("city".into(), Value::String("Beijing".into())),
("zip".into(), Value::Number(100000.0)),
])),
]);
println!("类型: {}", type_name(&data));
println!("节点数: {}", deep_count(&data));
if let Some(Value::String(name)) = find_key(&data, "name") {
println!("姓名: {}", name);
}
if let Some(Value::Array(scores)) = find_key(&data, "scores") {
let avg: f64 = scores.iter()
.filter_map(|v| if let Value::Number(n) = v { Some(*n) } else { None })
.sum::<f64>() / scores.len() as f64;
println!("平均分: {:.1}", avg);
}
if let Some(Value::Object(addr)) = find_key(&data, "address") {
if let Some(Value::String(city)) = find_key(&Value::Object(addr.clone()), "city") {
println!("城市: {}", city);
}
}
}
输出:
类型: object
节点数: 11
姓名: Alice
平均分: 91.7
城市: Beijing
用枚举模拟动态类型数据(如 JSON),通过模式匹配安全地提取和遍历。
find_key使用if let做安全访问,filter_map+ 模式匹配过滤并转换数组中的值。
❓ 常见问题
.. 匹配任意数量的元素(也忽略它们),_ 匹配单个位置。PartialOrd 的整数类型(i8-u128、u8-u128)、char。📖 小节
- match 守卫(if guard) 在模式匹配后追加条件判断,实现"模式 + 条件"的精细控制
- @ 绑定 在匹配特定模式时捕获值,避免重复计算或重新匹配
- | 多模式 让多个模式共享同一个分支代码,减少重复
- .. 和 ..= 范围匹配可以匹配整数区间和 char 区间,
..在解构中忽略剩余字段 - matches! 宏 返回布尔值,适合在 if 条件中做简洁的模式匹配判断
- 嵌套解构 允许在 match 中同时解构结构体、枚举、元组等多层复合类型
📝 作业
-
难度 ⭐:写一个函数
fn describe_number(n: i32) -> &'static str,使用 match + 范围匹配:1..=10返回 "small",11..=100返回 "medium",101..=1000返回 "large",其他返回 "out of range"。在 main 中测试 5、50、500、5000。 -
难度 ⭐⭐:定义一个枚举
Temperature,包含Celsius(f64)和Fahrenheit(f64)两个变体。写一个函数fn describe_temp(temp: &Temperature) -> &'static str,使用matches!宏和if守卫判断:Celsius 大于 30 或 Fahrenheit 大于 86 返回 "hot",Celsius 小于 0 或 Fahrenheit 小于 32 返回 "cold",否则返回 "moderate"。在 main 中测试 4 种温度。 -
难度 ⭐⭐⭐:定义一个结构体
Order,包含id: u32、items: Vec<String>、total: f64、status: OrderStatus(枚举:Pending、Shipped { tracking: String }、Delivered { date: String })。实现一个方法fn summary(&self) -> String,使用嵌套解构和 match 守卫,根据状态和总金额返回不同的摘要信息(如总金额 > 1000 的已发货订单显示 "High-value order shipped, tracking: xxx")。在 main 中创建 3 个订单并打印摘要。