Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

条件语句

在 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 块中通过 returnbreakpanic! 强制退出当前作用域。这使得后续代码可以放心地使用解构出来的变量。

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 collectionfor item in IntoIterator::into_iter(collection)转移所有权
for item in &collectionfor item in collection.iter()不可变借用
for item in &mut collectionfor 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. 核心应用:OptionResult

这是 match 在 Rust 中最高频的出现场景,用于安全地处理可能为空或可能出错的值。

fn main() {
    let res: Result<i32, &str> = Ok(200);
    match res {
        Ok(code) => println!("请求成功,状态码: {code}"),
        Err(msg) => println!("请求失败: {msg}"),
    }
}