条件语句
在 Rust 中,控制流和模式匹配不仅仅是逻辑的分叉口,它们更是安全性的守护者。Rust 编译器通过严格的类型检查和“穷尽性检查”,确保你在处理各种逻辑情况时不会留下漏洞。
以下是整理的 Rust 控制流、循环流与 match 表达式的详细指南。
一、控制流(Conditional Control Flow)
控制流是程序根据特定条件执行不同代码的能力。Rust 的 if 分支结构非常严谨。
1. if / else if / else
Rust 要求条件表达式必须是严格的 bool 类型。这意味着你不能像在 C 或 JavaScript 中那样使用数字(如 if (1))来代表逻辑真。这种设计避免了因隐式类型转换导致的逻辑错误。
fn main() {
let x = 10;
if x > 0 {
println!("positive");
} else if x == 0 {
println!("zero");
} else {
println!("negative");
}
}
2. if 是表达式:可以返回值
在 Rust 中,if 是一个表达式而不是语句。这意味着它可以产生一个值,并将其直接赋值给变量。
注意:所有分支返回的数据类型必须完全一致,且分支末尾不要写分号,否则该分支会返回单元类型 ()。
fn main() {
let x = 7;
// if 表达式赋值
let y = if x % 2 == 0 { 100 } else { 200 };
// let y = if x % 2 == 0 { 100 } else { 200; };
println!("y 的值是: {y}"); // 输出 200
}
3. if let:只关心某一种模式
当你只想处理某一种特定的模式(例如 Option 中的 Some),而对其他情况(如 None)不感兴趣时,if let 是比 match 更简洁的选择。它减少了样板代码的编写。
fn main() {
let v: Option<i32> = Some(10);
// 只解构 Some,忽略 None
if let Some(n) = v {
println!("解构成功,n = {n}");
} else {
println!("这里是 None 的情况");
}
}
4. let else:模式不匹配就提前退出
这是 Rust 1.65 引入的新语法,非常适合编写“守护语句(Guard Statement)”。如果在解构时失败,必须在 else 块中通过 return、break 或 panic! 强制退出当前作用域。这使得后续代码可以放心地使用解构出来的变量。
fn parse_first(v: Vec<i32>) -> Option<i32> {
// 如果无法获取第一个元素,直接返回 None
let Some(first) = v.get(0) else {
return None;
};
// 此时 first 已经成功绑定,且作用域在外面
Some(*first)
}
fn main() {
let numbers = vec![1, 2, 3];
if let Some(val) = parse_first(numbers) {
println!("第一个值是: {val}");
}
}
二、循环流(Looping Control Flow)
Rust 提供了三种循环原语,它们在底层性能上是一致的,但在语义表达上各有侧重。
1. loop:无限循环 + break 返回值
loop 常用于需要反复执行直到满足某个条件(如轮询任务或重试逻辑)的场景。由于 loop 保证一定会运行(直到被 break),它也可以作为表达式返回一个值。
fn main() {
let mut n = 0;
let result = loop {
n += 1;
if n == 5 {
break n * 2; // 带值跳出循环
}
};
println!("结果是: {result}"); // 10
}
2. while:条件循环
这是最传统的循环方式,每次迭代开始前都会检查条件。适合处理那些依赖外部状态变化的逻辑。
fn main() {
let mut n = 3;
while n > 0 {
println!("{n}...");
n -= 1;
}
println!("发射!");
}
3. for:遍历迭代器(最常用)
for 循环通过迭代器工作,是 Rust 中最安全的选择,因为它不会出现索引越界(Out of Bounds)的问题。
| 使用方法 | 等价使用方式 | 所有权 |
|---|---|---|
for item in collection | for item in IntoIterator::into_iter(collection) | 转移所有权 |
for item in &collection | for item in collection.iter() | 不可变借用 |
for item in &mut collection | for item in collection.iter_mut() | 可变借用 |
fn main() {
// 1. 范围遍历
for i in 0..3 { println!("范围 A: {i}"); } // 0, 1, 2
for i in 0..=3 { println!("范围 B: {i}"); } // 0, 1, 2, 3
// 2. 遍历集合(借用与移动)
let v = vec![10, 20, 30];
for x in &v { println!("借用元素: {x}"); } // v 依然可用
// 3. 可变借用遍历
let mut nums = vec![1, 2, 3];
for x in &mut nums {
*x *= 10; // 修改原始数据
}
// 4. 带索引遍历
for (i, val) in nums.iter().enumerate() {
println!("索引 {i} 的值是 {val}");
}
}
4. 循环控制与标签
continue:结束当前迭代,立即开始下一次。break:立即退出当前循环。- 循环标签 :在处理多层嵌套循环时,你可以给循环起名字(以单引号开头),以便在内层直接退出外层, 影响可读性。
fn main() {
'outer: for i in 0..10 {
'inner: for j in 0..10 {
if i + j == 5 {
println!("找到目标:i={}, j={}", i, j);
break 'outer; // 跳出最外层循环
}
}
}
}
三、match 表达式(Pattern Matching)
match 是 Rust 的“核心杀手锏”,它非常类似于多分支的 switch,但功能要强大得多。它强制要求 穷尽性检查 ,即你必须处理所有可能的情况。
1. 基本用法与模式
match 的每个分支被称为一个“臂(Arm)”。_ 是通配符,用于捕获所有未明确列出的情况。
fn main() {
let n = 3;
match n {
1 => println!("一"),
2 => println!("二"),
3 => println!("三"),
_ => println!("其他数字"), // 必须有这一行,除非 n 的所有可能已被覆盖
}
}
2. 范围与多重匹配
你可以使用 | 匹配多个值,或使用 ..= 匹配一个闭区间。
fn main() {
let score = 85;
match score {
0..=59 => println!("不及格"),
60..=80 => println!("合格"),
81..=100 => println!("优秀"),
_ => println!("无效分数"),
}
let day = 6;
match day {
1 | 2 | 3 | 4 | 5 => println!("工作日"),
6 | 7 => println!("周末"),
_ => println!("火星日?"),
}
}
3. 解构复合类型(元组/结构体/枚举)
match 最强大的地方在于它可以“拆解”数据结构。
struct Point { x: i32, y: i32 }
enum Message { Quit, Write(String) }
fn main() {
// 1. 解构元组
let pair = (0, -2);
match pair {
(0, y) => println!("在 Y 轴上: {y}"),
(x, 0) => println!("在 X 轴上: {x}"),
_ => println!("在象限内"),
}
// 2. 解构结构体
let p = Point { x: 10, y: 0 };
match p {
Point { x, y: 0 } => println!("X 轴上的点,x = {x}"),
Point { x, y } => println!("普通点 ({x}, {y})"),
}
// 3. 解构枚举
let msg = Message::Write(String::from("Hello"));
match msg {
Message::Quit => println!("退出"),
Message::Write(s) => println!("消息内容: {s}"),
}
}
4. 进阶:匹配守卫与 @ 绑定
- 匹配守卫 (Match Guard) :在模式匹配的基础上增加
if条件,用于更细粒度的过滤。 @绑定 :允许你在匹配一个值的同时,将其绑定到一个变量上,方便后续使用。
fn main() {
// 匹配守卫
let num = Some(10);
match num {
Some(x) if x > 5 => println!("大于 5 的数字: {x}"),
Some(x) => println!("普通数字: {x}"),
None => (),
}
// @ 绑定
let age = 7;
match age {
v @ 1..=12 => println!("小孩,年龄是: {v}"),
v @ 13..=19 => println!("青少年,年龄是: {v}"),
_ => println!("成年人"),
}
}
5. 核心应用:Option 和 Result
这是 match 在 Rust 中最高频的出现场景,用于安全地处理可能为空或可能出错的值。
fn main() {
let res: Result<i32, &str> = Ok(200);
match res {
Ok(code) => println!("请求成功,状态码: {code}"),
Err(msg) => println!("请求失败: {msg}"),
}
}