上次我们聊了Enum的艺术,这次直奔主题——模式匹配(Pattern Matching)的进阶玩法。如果你已经掌握了基本的match和if let,恭喜,你只是挠了痒痒。模式匹配是Rust的“模式识别”引擎,能让你写出零开销的复杂逻辑检查,避开boilerplate代码,还能让编译器帮你验证一切。为什么深入它?因为高级模式匹配让你的代码更表达力强:从解构嵌套数据,到条件守卫,再到切片和引用魔法。它不只用于Enum,还渗透到结构体、元组、数组等所有角落。读完这篇,你能自信地重构那些“if-else地狱”。走起!
1. 基础回顾:从简单到嵌套
先热身:基本match解构Enum或元组
#[derive(Debug)]
enum IpAddr {
V4(u8, u8, u8, u8),
V6(String),
}
fn main() {
let home = IpAddr::V4(127, 0, 0, 1);
match home {
IpAddr::V4(a, b, c, d) => println!("IPv4: {}.{}.{}.{}", a, b, c, d),
IpAddr::V6(addr) => println!("IPv6: {}", addr),
}
// 输出: IPv4: 127.0.0.1
}嵌套?试试结构体里的Enum:
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
#[derive(Debug)]
enum Shape {
Circle(Point, f64),
Rect(Point, Point),
}
fn describe_shape(shape: Shape) {
match shape {
Shape::Circle(center, radius) => {
println!("Circle at ({}, {}) with radius {}", center.x, center.y, radius);
}
Shape::Rect(start, end) => {
println!("Rect from ({}, {}) to ({}, {})", start.x, start.y, end.x, end.y);
}
}
}简单,但我们需要更强大:条件、绑定、忽略。2. 守卫(Guards):match条件判断match后加if条件,就是守卫!它让模式更精确,避免多余分支。示例:检查IP地址是否本地(127.x.x.x)。
fn is_local(ip: IpAddr) -> bool {
match ip {
IpAddr::V4(127, _, _, _) if true => true, // 守卫总是true,纯演示
IpAddr::V4(127, a, b, c) if a < 256 && b < 256 && c < 256 => true, // 实际守卫
_ => false,
}
}
fn main() {
let local = IpAddr::V4(127, 0, 0, 1);
println!("Is local: {}", is_local(local)); // 输出: Is local: true
}守卫用在复杂逻辑中神器:结合借用检查或计算,而不重复模式。小贴士:守卫求值在匹配后,所以模式先匹配,再if判断。性能零成本!3. @绑定:捕获部分模式@是Rust的“别名”魔法,让你绑定整个子模式,而不只是单个值。超级适合部分解构。示例:匹配IP并绑定前缀。
fn describe_ip(ip: IpAddr) {
match ip {
IpAddr::V4(prefix @ 127, a, b, c) => { // prefix绑定127
println!("Local IP: 127.{}.{}.{}", a, b, c);
}
IpAddr::V4(a, b, c, d) => {
println!("IPv4: {}.{}.{}.{}", a, b, c, d);
}
IpAddr::V6(addr) => println!("IPv6: {}", addr),
}
}
fn main() {
describe_ip(IpAddr::V4(127, 0, 0, 1));
// 输出: Local IP: 127.0.0.1
}@绑定嵌套用?无压力:
match some_complex_data {
Some(Ok(value @ Point { x: 0..=10, .. })) => println!("Small X: {:?}", value),
_ => {}
}这让你的match像“智能解包机”。4. 引用与可变模式:借用而不拥有模式匹配支持&(借用)和&mut(可变借用),完美契合Rust的所有权。示例:修改列表中的元素,而不转移所有权。
fn process_vec(vec: &mut Vec<i32>) {
for element in vec.iter_mut() { // iter_mut产生&mut引用
match element {
&mut 0 => *element = 10, // 解引用修改
&mut x if *x > 5 => *x *= 2,
_ => {}
}
}
}
fn main() {
let mut nums = vec![0, 3, 7, 1];
process_vec(&mut nums);
println!("{:?}", nums); // 输出: [10, 3, 14, 1]
}忽略模式用_或(..)(忽略结构体字段):
struct User {
name: String,
age: u32,
email: Option<String>,
}
match user {
User { name, age: .., .. } if age > 18 => println!("Adult: {}", name), // 忽略age和email
_ => {}
}5. 切片与数组模式:处理动态数据Rust的切片模式(slice patterns)是数组/Vec的杀手锏,能匹配头、中、尾。示例:检查Vec是否是[1, 2, 3]的变体。
fn describe_slice(slice: &[i32]) {
match slice {
[1, 2, 3] => println!("Exact: {:?}", slice),
[1, .., 3] => println!("Starts with 1, ends with 3: {:?}", slice), // 中间任意
[.., 0 | -1] => println!("Ends with 0 or -1"),
[42, second, rest @ ..] => { // rest绑定剩余切片
println!("42 followed by {} and rest: {:?}", second, rest);
}
[] => println!("Empty"),
_ => println!("Other"),
}
}
fn main() {
describe_slice(&[1, 5, 7, 3]); // 输出: Starts with 1, ends with 3: [1, 5, 7, 3]
describe_slice(&[42, 99]); // 输出: 42 followed by 99 and rest: []
}注意@ ..绑定剩余部分,像Python的切片但类型安全。6. 其他高级技巧:let、while let 与宏
let (x, y) = some_point; // 解构元组
let User { name, .. } = user; // 忽略其他字段let mut stack = vec![Some(1), Some(2), None, Some(3)];
while let Some(value) = stack.pop() {
println!("{}", value);
}
// 输出: 3, 2, 1for (i, &val) in vec.iter().enumerate() {
if val % 2 == 0 { println!("Even at {}: {}", i, val); }
}最后,模式匹配还能在宏中用(macro_rules!),但那是另一个话题了。结语:模式匹配,Rust的思考工具高级模式匹配不是语法糖,它是Rust让你“声明式思考”的方式:描述你想要什么,编译器确保安全。实践建议:拿个JSON解析器或状态机项目,重写用match。遇到卡壳?Rust的错误消息超友好,它会提示缺失分支。