
解构的艺术:把数据拆得明明白白
还记得你第一次用 match 的时候吗?大概是这样:
let x = Some();
match x {
Some(n) => println!("有值:{}", n),
None => println!("没值"),
}
是不是觉得……也就这样?
兄弟,那你可太小看 Rust 的模式匹配了!它不仅能匹配值,还能解构数据、守卫条件、嵌套匹配。今天咱们就聊聊这个让代码像诗歌一样优雅的神器。
模式是 Rust 中一种匹配值结构的方式。它告诉你:"如果数据长这样,就那样处理"。
生活化类比:
想象你去邮局寄包裹:
模式类型 | 示例 | 用途 |
|---|---|---|
字面量 | 0, true, "hello" | 匹配具体值 |
变量 | x, name | 绑定到新变量 |
通配符 | _ | 匹配任何值,不绑定 |
解构 | Some(x), (a, b) | 拆开复合类型 |
守卫 | n if n > 0 | 添加额外条件 |
范围 | 1..=10 | 匹配范围内的值 |
引用 | &x, &mut x | 匹配引用 |
fn main() {
let point = (, );
// 解构元组
let (x, y) = point;
println!("x = {}, y = {}", x, y); // x = 3, y = 5
}
看到没?一行代码就把元组拆开了!
struct Person {
name: String,
age: u32,
city: String,
}
fn main() {
let person = Person {
name: String::from("Alice"),
age: ,
city: String::from("Beijing"),
};
// 解构结构体
let Person { name, age, city } = person;
println!("{} 今年 {} 岁,住在 {}", name, age, city);
// 只解构部分字段
let person2 = Person {
name: String::from("Bob"),
age: ,
city: String::from("Shanghai"),
};
let Person { name, .. } = person2; // .. 表示忽略其他字段
println!("名字:{}", name);
}
说人话:
{ name, age, city }:把三个字段都拆出来,变量名和字段名一样{ name, .. }:只拆 name,其他不要了enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
fn main() {
let msg = Message::Write(String::from("Hello"));
match msg {
Message::Quit => println!("退出"),
Message::Move { x, y } => println!("移动到 ({}, {})", x, y),
Message::Write(text) => println!("消息:{}", text),
Message::ChangeColor(r, g, b) => println!("颜色:RGB({}, {}, {})", r, g, b),
}
}
看到没?枚举的每个变体都可以解构出里面的数据!
fn main() {
let num = Some();
match num {
Some(x) if x < => println!("小于 5:{}", x),
Some(x) => println!("大于等于 5:{}", x),
None => println!("没有值"),
}
}
说人话:
if x < 5 就是守卫——即使匹配了 Some(x),还要通过守卫的检查才行。
fn main() {
let x = ;
match x {
| | => println!("1 到 3"),
| | => println!("4 到 6"),
_ => println!("其他"),
}
}
| 相当于"或"——匹配 1、2、3 中的任何一个都行。
fn main() {
let age = ;
match age {
..= => println!("儿童"),
..= => println!("青少年"),
..= => println!("成年人"),
..= => println!("老年人"), // 60 及以上
}
}
注意: ..= 是闭区间(包含两端),.. 是开区间(不包含右端)。
struct Point {
x: i32,
y: i32,
}
enum Direction {
Up,
Down,
Left,
Right,
}
fn main() {
let data = (Point { x: , y: }, Direction::Up);
match data {
(Point { x: , y: }, _) => println!("原点,方向无所谓"),
(Point { x, y }, Direction::Up) => println!("向上,位置 ({}, {})", x, y),
(Point { x, y }, Direction::Down) => println!("向下,位置 ({}, {})", x, y),
_ => println!("其他情况"),
}
}
看到没?模式可以嵌套——元组里套结构体,结构体里套枚举,想多深就多深!
fn main() {
let num = Some();
match num {
Some(n @ ..=) => println!("1 到 10 之间的数:{}", n),
Some(n) => println!("其他数:{}", n),
None => println!("没有值"),
}
}
说人话:
n @ 1..=10 的意思是:匹配 1 到 10 的范围,同时把这个值绑定到变量 n 上。
fn main() {
let x = ;
let ref_x = &x;
match ref_x {
&val => println!("值是:{}", val),
}
// 或者用 ref 关键字
let y = Some(String::from("hello"));
match y {
Some(ref s) => println!("字符串:{}", s), // s 是 &String
None => println!("没有值"),
}
}
注意:
&val:匹配引用,解引用ref s:匹配值,但绑定为引用fn main() {
let x = Some();
// ❌ 编译错误
match x {
Some(n) => println!("有值:{}", n),
// 忘了处理 None!
}
}
编译器在说什么人话?
"你漏了 None 的情况!Rust 要求你处理所有可能。"
解决方案:
match x {
Some(n) => println!("有值:{}", n),
None => println!("没值"),
}
或者用 _ 兜底:
match x {
Some(n) => println!("有值:{}", n),
_ => println!("其他情况"),
}
fn main() {
let num = Some();
// ❌ 这样写有问题
match num {
Some(x) => println!("任何 Some"),
Some(x) if x > => println!("大于 10"), // 永远到不了这里!
_ => println!("其他"),
}
}
问题: 第一个 Some(x) 已经匹配了所有 Some,后面的守卫永远不会执行。
正确写法:
match num {
Some(x) if x > => println!("大于 10"),
Some(x) => println!("其他 Some"),
_ => println!("其他"),
}
记住: 守卫条件要放在前面!
fn main() {
let s = String::from("hello");
let opt = Some(s);
// ❌ 这样写会移动 s
match opt {
Some(inner) => println!("{}", inner), // s 被移动了
None => (),
}
// println!("{}", s); // ❌ 编译错误:s 已被移动
}
解决方案:
match &opt {
Some(inner) => println!("{}", inner), // 借用,不移动
None => (),
}
fn main() {
let s = String::from("hello");
let opt = Some(s);
// 正确理解:
match opt {
Some(ref inner) => {
// inner 的类型是 &String(借用)
println!("{}", inner);
},
None => (),
}
// opt 还可以用,因为只是借用
println!("{:?}", opt);
}
记住:
&inner:匹配引用,inner 是值ref inner:匹配值,inner 是引用enum ConfigValue {
String(String),
Number(i32),
Boolean(bool),
List(Vec<ConfigValue>),
}
fn print_config(key: &str, value: &ConfigValue) {
match value {
ConfigValue::String(s) => println!("{} = \"{}\"", key, s),
ConfigValue::Number(n) => println!("{} = {}", key, n),
ConfigValue::Boolean(b) => println!("{} = {}", key, if *b { "true" } else { "false" }),
ConfigValue::List(items) => {
println!("{} = [", key);
for item in items {
print_config(" ", item);
}
println!("]");
}
}
}
fn main() {
let config = ConfigValue::List(vec![
ConfigValue::String(String::from("host")),
ConfigValue::Number(),
ConfigValue::Boolean(true),
]);
print_config("config", &config);
}
enum OrderState {
Created,
Paid { amount: f64 },
Shipped { tracking: String },
Delivered,
Cancelled { reason: String },
}
fn handle_order(state: OrderState) {
match state {
OrderState::Created => {
println!("订单已创建,等待付款");
}
OrderState::Paid { amount } => {
println!("已付款 {:.2} 元,准备发货", amount);
}
OrderState::Shipped { tracking } => {
println!("已发货,单号:{}", tracking);
}
OrderState::Delivered => {
println!("已送达,感谢购买!");
}
OrderState::Cancelled { reason } => {
println!("订单已取消:{}", reason);
}
}
}
fn main() {
let order = OrderState::Paid { amount: 99.99 };
handle_order(order);
}
#[derive(Debug)]
struct User {
id: u32,
name: String,
role: Role,
}
#[derive(Debug)]
enum Role {
Admin,
User { permissions: Vec<String> },
Guest,
}
fn check_access(user: User) {
match user {
User { role: Role::Admin, .. } => {
println!("管理员,拥有所有权限");
}
User { name, role: Role::User { permissions }, .. } => {
println!("用户 {} 的权限:", name);
for perm in permissions {
println!(" - {}", perm);
}
}
User { name, role: Role::Guest, .. } => {
println!("访客 {},权限受限", name);
}
}
}
fn main() {
let user = User {
id: ,
name: String::from("Alice"),
role: Role::User {
permissions: vec![
String::from("read"),
String::from("write"),
],
},
};
check_access(user);
}

金句回顾:
if 让匹配更精确下篇预告:
咱们已经学了 Cargo 的基础用法(cargo new、cargo build、cargo run),但那只是冰山一角。下篇深入聊聊包管理 Cargo,看看怎么管理依赖、配置项目、发布 crate,让你成为 Cargo 大师!