模式匹配是 Rust 类型系统和控制流的核心特性,它允许开发者通过 “模式” 描述数据的结构,并根据匹配结果执行相应逻辑。相比其他语言的条件判断(如 if-elseswitch),Rust 的模式匹配更具表现力、安全性和简洁性,尤其在处理枚举、结构体等复合类型时优势显著。

一、模式匹配的基础概念与核心语法

1. 什么是模式?

模式是 Rust 中用于 “描述数据结构” 的语法片段,它可以匹配:

  • 字面量(如 10"hello"

  • 变量(如 xname

  • 复合类型(如枚举变体、结构体、元组)

  • 通配符(如 _

模式的核心作用是 “解构数据” 与 “判断匹配”:通过模式提取数据中的部分值,并检查数据是否符合预期结构。

2. 模式匹配的核心载体:match 表达式

match 是 Rust 中最完整的模式匹配语法,它要求穷尽所有可能的匹配情况(安全性保障),语法结构如下:

match 表达式 {

   模式1 => 执行逻辑1,

   模式2 => 执行逻辑2,

   // ... 所有可能的模式

}
基础示例:匹配枚举
enum Direction {

   Up,

   Down,

   Left,

   Right,

}

fn print\_direction(dir: Direction) {

   match dir {

       Direction::Up => println!("向上移动"),

       Direction::Down => println!("向下移动"),

       Direction::Left => println!("向左移动"),

       Direction::Right => println!("向右移动"),

   }

}

let dir = Direction::Right;

print\_direction(dir); // 输出:向右移动
核心规则:
  1. 穷尽性(Exhaustiveness)match 必须覆盖所有可能的匹配情况,否则编译报错(避免遗漏逻辑)。

  2. 不可达性(Unreachability):若后续模式永远无法匹配(如字面量 5 后接通配符 _),编译器会警告 “不可达代码”。

  3. 匹配顺序:自上而下匹配,一旦命中某个模式,后续模式不再执行(类似 switchbreak)。

二、常见模式类型与应用场景

Rust 支持多种模式类型,可根据数据结构和需求灵活选择。

1. 字面量模式(Literal Patterns)

匹配具体的字面量值(如整数、字符串、布尔值),适用于明确判断固定值的场景。

fn check\_number(n: i32) {

   match n {

       0 => println!("这是零"),

       1 | 2 | 3 => println!("这是1、2或3"), // 多字面量匹配(用 | 分隔)

       4..=9 => println!("这是4到9之间的数"), // 范围匹配(闭区间,仅支持可比较类型)

       \_ => println!("其他数字"), // 通配符,匹配所有未覆盖的情况

   }

}

check\_number(0);    // 输出:这是零

check\_number(2);    // 输出:这是1、2或3

check\_number(7);    // 输出:这是4到9之间的数

check\_number(10);   // 输出:其他数字

注意:范围模式仅支持实现 PartialOrd 特质的类型(如 i32char),且必须是 “连续的”(如 'a'..='z' 合法,1..=3 合法,但 1..=4 中跳过 2 不合法)。

2. 变量模式(Variable Patterns)

匹配任意值并绑定到变量,适用于需要提取数据的场景。若变量名与外部变量重名,会发生 “变量遮蔽”(Shadowing)。

let x = 10;

match x {

   // 绑定x的值到变量y(此时y=10,遮蔽外部x)

   y => println!("匹配到的值:{}", y),

}

// 外部x仍可用(遮蔽仅作用于match块内)

println!("外部x的值:{}", x); // 输出:外部x的值:10
应用:提取枚举关联数据
enum Option\<T> {

   Some(T),

   None,

}

let some\_value = Some(42);

match some\_value {

   // 绑定Some中的值到变量v

   Some(v) => println!("提取到的值:{}", v),

   None => println!("无值"),

}

3. 解构模式(Destructuring Patterns)

用于解构复合类型(结构体、元组、枚举),直接提取内部字段或关联数据,避免手动访问字段的冗余代码。

(1)解构元组
let point = (3, 5);

match point {

   // 解构元组,绑定两个元素到x和y

   (x, y) => println!("坐标:({}, {})", x, y),

}

// 部分解构:仅关注需要的字段,其他用通配符

match point {

   (x, \_) => println!("x坐标:{}", x),

}

// 带条件的解构:匹配x=0的情况

match point {

   (0, y) => println!("在y轴上,y={}", y),

   (x, 0) => println!("在x轴上,x={}", x),

   (x, y) => println!("在平面内,({}, {})", x, y),

}
(2)解构结构体
struct User {

   username: String,

   age: u32,

   is\_vip: bool,

}

let user = User {

   username: String::from("alice"),

   age: 28,

   is\_vip: true,

};

match user {

   // 解构结构体,绑定字段到变量(字段名需与定义一致)

   User { username, age, is\_vip } => {

       println!("用户名:{}", username);

       println!("年龄:{}", age);

       println!("是否VIP:{}", is\_vip);

   }

}

// 部分解构:仅关注username和is\_vip,age用通配符

match user {

   User { username, is\_vip, .. } => { // .. 表示“剩余所有字段”

       println!("用户名:{}", username);

       println!("是否VIP:{}", is\_vip);

   }

}

// 重命名字段:将username重命名为name,避免与外部变量冲突

let username = String::from("bob");

match user {

   User { username: name, age, .. } => {

       println!("用户的用户名:{}", name); // 输出:用户的用户名:alice

       println!("外部用户名:{}", username); // 输出:外部用户名:bob

   }

}
(3)解构枚举变体
enum Message {

   Quit,

   Move { x: i32, y: i32 },

   Write(String),

   ChangeColor(u8, u8, u8),

}

let msg = Message::Write(String::from("hello"));

match msg {

   Message::Quit => println!("退出消息"),

   // 解构结构体变体

   Message::Move { x, y } => println!("移动到:({}, {})", x, y),

   // 解构带数据的变体

   Message::Write(content) => println!("写入内容:{}", content),

   // 解构元组式变体

   Message::ChangeColor(r, g, b) => println!("颜色:RGB({}, {}, {})", r, g, b),

}

4. 通配符模式(Wildcard Patterns)

用于 “忽略不需要的部分”,避免冗余代码,常见形式有:

  • _:忽略单个值

  • ..:忽略结构体 / 元组的剩余字段(仅用于解构)

示例 1:忽略单个值
let (a, \_, c) = (10, 20, 30);

println!("a={}, c={}", a, c); // 输出:a=10, c=30
示例 2:忽略结构体剩余字段
struct Point3D {

   x: i32,

   y: i32,

   z: i32,

}

let p = Point3D { x: 1, y: 2, z: 3 };

match p {

   Point3D { x, .. } => println!("x坐标:{}", x), // 忽略y和z

}
示例 3:match 中覆盖所有情况
let n: i32 = 5;

match n {

   1 => println!("一"),

   2 => println!("二"),

   \_ => println!("其他数字"), // 覆盖所有未匹配的情况,避免编译报错

}

5. 守卫模式(Guard Patterns)

在模式后添加 if 条件,进一步过滤匹配结果,实现 “模式 + 条件判断” 的组合逻辑。

fn check\_age(age: u32) {

   match age {

       // 匹配18-60之间的年龄(闭区间),且通过守卫条件过滤

       a if a >= 18 && a <= 60 => println!("成年且未退休,年龄:{}", a),

       a if a < 18 => println!("未成年,年龄:{}", a),

       \_ => println!("已退休,年龄:{}", age),

   }

}

check\_age(25);  // 输出:成年且未退休,年龄:25

check\_age(17);  // 输出:未成年,年龄:17

check\_age(65);  // 输出:已退休,年龄:65
应用:结合枚举与守卫
let some\_number = Some(15);

match some\_number {

   Some(n) if n % 2 == 0 => println!("偶数:{}", n),

   Some(n) => println!("奇数:{}", n),

   None => println!("无值"),

}

6. @ 绑定模式(At Patterns)

用于 “绑定值到变量” 的同时,检查值是否符合模式,语法为 变量 @ 模式。适用于需要同时 “判断结构” 和 “保留原始值” 的场景。

enum Size {

   Small(u32),

   Medium(u32),

   Large(u32),

}

let size = Size::Medium(42);

match size {

   // 绑定整个变体到变量s,同时检查变体类型和内部值的范围

   s @ Size::Medium(n) if n >= 40 && n <= 50 => {

       println!("匹配到Medium尺寸,原始值:{:?}", s); // 输出:匹配到Medium尺寸,原始值:Medium(42)

       println!("尺寸值:{}", n); // 输出:尺寸值:42

   }

   Size::Small(n) => println!("Small尺寸,值:{}", n),

   Size::Large(n) => println!("Large尺寸,值:{}", n),

}
另一个示例:绑定字符串字面量
let name = String::from("alice");

match name.as\_str() {

   // 绑定值到变量s,同时检查是否为"alice"或"bob"

   s @ "alice" | s @ "bob" => println!("用户名:{}", s),

   \_ => println!("未知用户名"),

}

三、模式匹配的高级特性

1. 嵌套模式(Nested Patterns)

模式可以嵌套,用于解构多层嵌套的数据结构(如 “枚举包含结构体”“结构体包含元组”)。

// 嵌套数据结构:枚举包含结构体,结构体包含元组

enum Order {

   Purchase {

       id: u32,

       items: Vec<(String, u32)>, // (商品名, 数量)

   },

   Cancel(u32), // 取消订单,关联订单ID

}

let order = Order::Purchase {

   id: 1001,

   items: vec!\[(String::from("苹果"), 2), (String::from("香蕉"), 3)],

};

match order {

   // 嵌套解构:先匹配Purchase变体,再解构id和items,最后解构items中的元组

   Order::Purchase {

       id,

       items: \[(item1, qty1), (item2, qty2), ..], // 解构前两个商品

   } => {

       println!("订单ID:{}", id);

       println!("商品1:{} x {}", item1, qty1);

       println!("商品2:{} x {}", item2, qty2);

   }

   Order::Cancel(id) => println!("取消订单ID:{}", id),

}

2. 引用模式(Reference Patterns)

当匹配引用类型时,可通过 & 直接解构引用背后的值,避免手动解引用(*)。

let x = 10;

let ref\_x = \&x;

// 方式1:手动解引用

match ref\_x {

   \&val => println!("值:{}", val), // 输出:值:10

}

// 方式2:引用模式(更简洁)

match ref\_x {

   val => println!("值:{}", \*val), // 输出:值:10

}

// 可变引用模式

let mut y = 20;

let ref\_mut\_y = \&mut y;

match ref\_mut\_y {

   // 绑定可变引用到val,修改val即修改y

   val => \*val += 5,

}

println!("y的值:{}", y); // 输出:y的值:25

3. 常量模式(Constant Patterns)

匹配常量值(需用 const 定义),适用于需要匹配固定常量的场景。

const MAX\_AGE: u32 = 120;

const MIN\_AGE: u32 = 0;

fn check\_valid\_age(age: u32) {

   match age {

       MIN\_AGE => println!("年龄为最小值:{}", MIN\_AGE),

       MAX\_AGE => println!("年龄为最大值:{}", MAX\_AGE),

       a if a > MIN\_AGE && a < MAX\_AGE => println!("有效年龄:{}", a),

       \_ => println!("无效年龄:{}", age),

   }

}

check\_valid\_age(0);    // 输出:年龄为最小值:0

check\_valid\_age(120);  // 输出:年龄为最大值:120

check\_valid\_age(50);   // 输出:有效年龄:50

check\_valid\_age(121);  // 输出:无效年龄:121

四、模式匹配的其他载体

除了 match 表达式,Rust 还在多个场景中支持模式匹配,进一步扩展了其应用范围。

1. if let 表达式:简化单一模式匹配

当仅需关注 “一种匹配情况”,而忽略其他情况时,if letmatch 的简化语法,避免冗余的 _ => ()

基础语法:
if let 模式 = 表达式 {

   匹配成功时执行的逻辑

} else {

   // 可选:匹配失败时执行的逻辑

}
示例 1:匹配枚举变体
let some\_value: Option\<i32> = Some(5);

// 仅关注Some变体,忽略None

if let Some(v) = some\_value {

   println!("提取到的值:{}", v); // 输出:提取到的值:5

} else {

   println!("无值");

}
示例 2:结合守卫条件
let age = 25;

if let a @ 18..=60 = age {

   println!("成年且未退休,年龄:{}", a); // 输出:成年且未退休,年龄:25

}

2. while let 循环:基于模式的循环

当需要 “循环处理数据,直到模式不匹配” 时(如处理链表、迭代器),while let 非常适用。

示例:处理链表
enum List {

   Cons(i32, Box\<List>),

   Nil,

}

use List::{Cons, Nil};

let mut list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));

// 循环提取链表中的值,直到匹配到Nil

while let Cons(val, next) = list {

   println!("链表值:{}", val);

   list = \*next; // 更新链表为下一个节点

}

// 输出\</doubaocanvas>

在这里插入图片描述

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐