Rust 控制流

1 阅读6分钟

Rust 控制流

难度:⭐⭐ | 主题:控制流(Control Flow)

1️⃣ 原理层:Rust 控制流核心概念

控制流是表达式,不是语句

Rust 中几乎所有控制流结构都是表达式(返回值),而不是语句:

  • if 表达式可以赋值给变量
  • match 表达式返回最后一个表达式的值
  • loop 可以通过 break value 返回值

这使得 Rust 代码更加函数式和声明式。

Match 模式匹配原理
match value {
    pattern1 => expression1,
    pattern2 => expression2,
    _ => default_expression,
}
  • 模式(Pattern):可以是字面量、变量、元组、枚举、引用等
  • 守卫(Guard):使用 if 添加额外条件:pattern if condition => expr
  • 绑定(Binding):使用 @ 操作符同时匹配和绑定值
穷尽性检查(Exhaustiveness Checking)

Rust 编译器强制要求 match 表达式必须覆盖所有可能的值:

  • 编译时检查,防止逻辑漏洞
  • _ 模式作为兜底(catch-all)
  • 非穷尽的 match 会导致编译错误

2️⃣ 实战层:完整代码示例

2.1 if/else 表达式
fn main() {
    let number = 6;
    
    // 基本 if
    if number % 4 == 0 {
        println!("number is divisible by 4");
    } else if number % 3 == 0 {
        println!("number is divisible by 3");
    } else {
        println!("number is not divisible by 4 or 3");
    }
    
    // if 作为表达式
    let condition = true;
    let result = if condition {
        5
    } else {
        6
    };
    println!("result: {}", result); // result: 5
    
    // ❌ 错误:分支类型不一致
    // let inconsistent = if condition {
    //     5
    // } else {
    //     "six"  // 编译错误:expected integer, found &str
    // };
}
2.2 loop 循环
fn main() {
    let mut count = 0;
    
    // 基本 loop
    loop {
        count += 1;
        if count == 3 {
            break;  // 退出循环
        }
    }
    
    // loop 返回值
    let result = loop {
        count += 1;
        if count == 10 {
            break count * 2;  // 返回 20
        }
    };
    println!("loop result: {}", result);
    
    // 带标签的 loop
    let mut x = 0;
    'outer: loop {
        x += 1;
        'inner: loop {
            if x == 2 {
                break 'outer;  // 跳出外层循环
            }
            break 'inner;
        }
    }
    
    // loop + break with value
    let mut counter = 0;
    let result = loop {
        counter += 1;
        if counter == 10 {
            break counter * 2;
        }
    };
    println!("Final result: {}", result);  // 20
}
2.3 while 循环
fn main() {
    let mut number = 3;
    
    // 基本 while
    while number != 0 {
        println!("{}!", number);
        number -= 1;
    }
    println!("LIFTOFF!!!");
    
    // while 遍历数组
    let a = [10, 20, 30, 40, 50];
    let mut index = 0;
    
    while index < 5 {
        println!("value: {}", a[index]);
        index += 1;
    }
}
2.4 for 循环
fn main() {
    // for 遍历范围
    for number in 1..4 {
        println!("{}!", number);
    }
    println!("LIFTOFF!!!");
    
    // for 遍历数组
    let a = [10, 20, 30, 40, 50];
    for element in a {
        println!("value: {}", element);
    }
    
    // for 反向遍历
    for number in (1..4).rev() {
        println!("{}!", number);
    }
    
    // for 带索引
    for (index, value) in a.iter().enumerate() {
        println!("index: {}, value: {}", index, value);
    }
    
    // for 遍历字符串
    for c in "Hello".chars() {
        println!("{}", c);
    }
    
    // for 遍历字节
    for b in "Hello".bytes() {
        println!("{}", b);
    }
}
2.5 match 表达式
fn main() {
    // 基本 match
    let x = 5;
    match x {
        1 => println!("one"),
        2 => println!("two"),
        3 => println!("three"),
        _ => println!("something else"),  // 兜底
    }
    
    // match 返回值
    let x = 5;
    let y = match x {
        1 => "one",
        2 => "two",
        3 => "three",
        _ => "other",
    };
    println!("y = {}", y);
    
    // match 元组
    let point = (3, 4);
    match point {
        (0, 0) => println!("Origin"),
        (0, y) => println!("On y-axis at {}", y),
        (x, 0) => println!("On x-axis at {}", x),
        (x, y) => println!("On ({}, {})", x, y),
    }
    
    // match 枚举
    enum Coin {
        Penny,
        Nickel,
        Dime,
        Quarter,
    }
    
    fn value_in_cents(coin: Coin) -> u8 {
        match coin {
            Coin::Penny => 1,
            Coin::Nickel => 5,
            Coin::Dime => 10,
            Coin::Quarter => 25,
        }
    }
    
    // match 守卫
    let num = Some(4);
    match num {
        Some(x) if x % 2 == 0 => println!("Even number: {}", x),
        Some(x) => println!("Odd number: {}", x),
        None => println!("No number"),
    }
    
    // @ 绑定
    let name = Some(String::from("Alice"));
    match name {
        Some(ref n @ String::from("Alice")) => println!("Hello {}", n),
        Some(n) => println!("Hello {}", n),
        None => println!("No name"),
    }
}
2.6 if let 表达式
fn main() {
    // if let 简化 match
    let some_u8_value = Some(0u8);
    
    // 等价于:
    // match some_u8_value {
    //     Some(3) => println!("three"),
    //     _ => (),
    // }
    if let Some(3) = some_u8_value {
        println!("three");
    }
    
    // if let else
    let config_max = Some(3u8);
    let mut max = 0u8;
    
    if let Some(max_val) = config_max {
        max = max_val;
    } else {
        max = 10;
    }
    println!("max: {}", max);
    
    // if let 多个模式(Rust 1.53+)
    let x = 4;
    if let 1 | 2 | 3 = x {
        println!("one, two, or three");
    }
    
    // while let
    let mut stack = vec![1, 2, 3];
    while let Some(top) = stack.pop() {
        println!("{}", top);
    }
    
    // for 与 if let 结合
    let pairs = vec![(1, 2), (3, 4), (5, 6)];
    for (x, y) in pairs {
        if let (1, _) = (x, y) {
            println!("First is 1");
        }
    }
}

3️⃣ 最佳实践

3.1 选择合适的控制流结构
场景推荐结构
已知迭代次数for 循环
条件未知的循环while 循环
无限循环loop + break
模式匹配match
单模式匹配if let
值的条件分支if/else
3.2 避免无限循环
// ❌ 危险:可能无限循环
loop {
    // 没有 break 条件
}

// ✅ 安全:有明确退出条件
loop {
    if condition {
        break;
    }
}

// ✅ 使用 while 更清晰
while !condition {
    // ...
}
3.3 match vs if let 选择

使用 match

  • 需要匹配多个模式
  • 需要穷尽性检查
  • 所有分支都重要
match option {
    Some(value) => handle(value),
    None => handle_none(),
}

使用 if let

  • 只关心一个模式
  • 其他情况不需要处理或统一处理
  • 代码更简洁
if let Some(value) = option {
    handle(value);
}
// 其他情况忽略
3.4 表达式风格

优先使用表达式风格,使代码更函数式:

// ✅ 表达式风格
let status = if score > 60 { "pass" } else { "fail" };

// ❌ 语句风格(冗余)
let status;
if score > 60 {
    status = "pass";
} else {
    status = "fail";
}

4️⃣ 问题诊断

4.1 编译错误:类型不匹配
// ❌ 错误
let x = if true {
    5
} else {
    "five"  // error: if and else have incompatible types
};

// ✅ 修复:统一类型
let x = if true {
    5
} else {
    0
};
4.2 非穷尽 match
// ❌ 错误
enum Direction {
    Up,
    Down,
    Left,
    Right,
}

fn move_player(dir: Direction) {
    match dir {
        Direction::Up => println!("Up"),
        Direction::Down => println!("Down"),
        // error: non-exhaustive patterns: `Left` and `Right` not covered
    }
}

// ✅ 修复 1:覆盖所有模式
fn move_player_fixed(dir: Direction) {
    match dir {
        Direction::Up => println!("Up"),
        Direction::Down => println!("Down"),
        Direction::Left => println!("Left"),
        Direction::Right => println!("Right"),
    }
}

// ✅ 修复 2:使用 _ 兜底
fn move_player_default(dir: Direction) {
    match dir {
        Direction::Up => println!("Up"),
        Direction::Down => println!("Down"),
        _ => println!("Other direction"),
    }
}
4.3 无限循环警告
// ⚠️ 警告:denying some lints due to unknown or unstable features
#[warn(unconditional_recursion)]
fn recurse() {
    recurse();  // warning: function cannot return
}

// ✅ 修复:添加终止条件
fn recurse_fixed(n: u32) {
    if n > 0 {
        recurse_fixed(n - 1);
    }
}
4.4 if let 与变量遮蔽
// ⚠️ 注意:内部变量遮蔽外部变量
let x = 5;
if let Some(x) = Some(10) {
    println!("{}", x);  // 10,不是 5
}
println!("{}", x);  // 5

// ✅ 明确命名避免混淆
let original_x = 5;
if let Some(new_x) = Some(10) {
    println!("{}", new_x);
}

5️⃣ 权威引用


🎯 学习要点总结

  • ✅ 控制流结构在 Rust 中都是表达式(返回值)
  • match 提供强大的模式匹配和编译时穷尽性检查
  • if let 是处理单模式的简洁语法
  • ✅ 选择合适的控制流结构提高代码可读性
  • ✅ 注意类型一致性和模式穷尽性

📝 练习题

基础练习

  1. FizzBuzz:使用 for 循环打印 1-100,3 的倍数打印 Fizz,5 的倍数打印 Buzz,同时是 3 和 5 的倍数打印 FizzBuzz

  2. 成绩等级:使用 match 将分数转换为等级(90-100: A, 80-89: B, 70-79: C, 60-69: D, <60: F)

进阶练习

  1. 斐波那契数列:使用 loop 生成前 20 个斐波那契数

  2. 选项处理器:使用 if letwhile let 处理 Option

  3. 自定义枚举匹配:定义一个 TrafficLight 枚举,使用 match 处理不同颜色的行为