Rust: Rust 流程控制:if-else、match 与 if let 完全指南

最后更新:2026-08-26

Rust 的 match 不是 switch——它更强大,因为编译器会检查你是否覆盖了所有情况。

控制流是程序的骨架。Rust 除了有传统的 if/else,还有两大王牌:match 模式匹配if let 语法糖


1. 你将学到


2. 猜拳游戏裁判的故事

(1) 痛苦:用 if-else 写猜拳判定

Emma 是一名游戏开发者,她想在游戏中加入石头剪刀布功能:

TEXT 📖 仅展示
玩家出:石头(1)、剪刀(2)、布(3)
电脑出:随机一个
需要判定:赢、输、还是平局

if/else 判定的代码:

TEXT 📖 仅展示
if 玩家 == 1 && 电脑 == 1 → 平局
else if 玩家 == 1 && 电脑 == 2 → 赢
else if 玩家 == 1 && 电脑 == 3 → 输
... 还需要写 6 个分支!

写了一大堆 if/else,容易漏掉某些组合,而且难以阅读。

(2) Rust match 的优雅解法

RUST
fn main() {
    let player = 1;    // 石头
    let computer = 2;  // 剪刀

    let result = match (player, computer) {
        (1, 1) | (2, 2) | (3, 3) => "平局",
        (1, 2) | (2, 3) | (3, 1) => "你赢了!",
        (1, 3) | (2, 1) | (3, 2) => "你输了!",
        _ => "无效出拳",
    };

    println!("结果: {}", result);
}

match 在这就像一个裁判:它把玩家和电脑的出拳组合一一对应,没有遗漏,没有歧义。编译器会检查你是否写了所有 9 种组合。


3. if/else 条件分支

(1) 基本用法

RUST
fn main() {
    let number = 7;

    if number < 5 {
        println!("数字小于 5");
    } else if number == 5 {
        println!("数字等于 5");
    } else {
        println!("数字大于 5");
    }
}

(2) if 是表达式——可以返回值

RUST
let condition = true;
let value = if condition { 5 } else { 6 };
// 注意:两个分支必须是同一类型
println!("value = {}", value);  // value = 5

重要if 的两个分支必须是同一种类型。if true { 5 } else { "six" } 会编译报错。


4. match 模式匹配

(1) 基本语法

100%
graph TB
    A[match 表达式] --> B[每一条分支 = 一个模式]
    B --> C[模式匹配成功 → 执行该分支]
    B --> D[模式不匹配 → 尝试下一个分支]
    A --> E[最后必须有 _ 通配符兜底]
RUST
let number = 3;
match number {
    1 => println!("one"),
    2 => println!("two"),
    3 => println!("three"),
    _ => println!("other"),  // 通配符——匹配所有剩余情况
}

(2) match 关键规则

规则 说明
穷尽性 必须覆盖所有可能的值,缺少分支会编译报错
通配符 _ 匹配所有剩余情况,放在最后
多模式 | 一个分支匹配多个模式:1 | 2 =>
范围匹配 1..=5 匹配 1 到 5 的闭区间
守卫 guard 额外条件:x if x > 5 =>

(3) if 与 match 对比

特性 if/else match
匹配方式 布尔条件 模式匹配
穷尽性检查 无(else 可省略) 强制(必须覆盖所有值)
解构能力 支持解构枚举、元组、结构体
多值匹配 需要 `
适用场景 范围判断、布尔逻辑 枚举处理、值分类、解构

5. if let 语法糖

当只关心某个特定模式、不关心其他值时,if letmatch 更简洁:

RUST
let optional = Some(5);

// match 写法(啰嗦)
match optional {
    Some(value) => println!("值为: {}", value),
    _ => (),  // 必须写这个空分支
}

// if let 写法(简洁)
if let Some(value) = optional {
    println!("值为: {}", value);
    // 不需要写 _ => ()
}

(4) 控制流关键字速查

关键字/语法 用途 示例
if / else if / else 条件分支 if x > 0 { ... } else { ... }
match 模式匹配 match x { 1 => ..., _ => ... }
if let 单模式匹配语法糖 if let Some(v) = x { ... }
while 条件循环 while x > 0 { ... }
while let 模式匹配循环 while let Some(v) = iter.next() { ... }
loop 无限循环 loop { ... break; }
for..in 迭代器遍历 for item in collection { ... }
break 跳出循环 break;break value;
continue 跳过本次迭代 continue;

6. 完整示例

▶ 示例 1:if/else 成绩分级(难度 ⭐)

RUST
// ============================================
// 用 if 表达式给成绩分级并返回字符串
// ============================================

fn main() {
    let score = 88;

    let grade = if score >= 90 {
        "优秀 (A)"
    } else if score >= 80 {
        "良好 (B)"
    } else if score >= 70 {
        "中等 (C)"
    } else if score >= 60 {
        "及格 (D)"
    } else {
        "不及格 (F)"
    };

    println!("分数: {}, 等级: {}", score, grade);
}

输出:

TEXT 📖 仅展示
分数: 88, 等级: 良好 (B)

if 表达式的每个分支都必须返回相同类型。这里所有分支都返回 &str,所以编译器高兴。


▶ 示例 2:match 处理 HTTP 状态码(难度 ⭐⭐)

RUST
// ============================================
// 用 match 优雅处理 HTTP 状态码
// ============================================

fn handle_status_code(code: u16) -> &'static str {
    match code {
        200 => "OK: 请求成功",
        201 => "Created: 资源已创建",
        204 => "No Content: 无返回内容",
        301 | 302 => "Redirect: 资源已移动",
        400 => "Bad Request: 请求格式错误",
        401 => "Unauthorized: 未授权",
        403 => "Forbidden: 禁止访问",
        404 => "Not Found: 资源不存在",
        500 => "Internal Server Error: 服务器内部错误",
        502 | 503 => "Server Error: 服务器暂时不可用",
        _ => "Unknown: 未知状态码",
    }
}

fn main() {
    let codes = [200, 404, 418, 500];
    for &code in codes.iter() {
        println!("状态码 {}: {}", code, handle_status_code(code));
    }
}

输出:

TEXT 📖 仅展示
状态码 200: OK: 请求成功
状态码 404: Not Found: 资源不存在
状态码 418: Unknown: 未知状态码
状态码 500: Internal Server Error: 服务器内部错误

| 操作符可以让一个分支匹配多个模式,避免重复代码。_ 通配符确保所有状态码都被覆盖。


▶ 示例 3:match 守卫和 if let(难度 ⭐⭐)

RUST
// ============================================
// match 守卫 + if let 简化模式匹配
// ============================================

fn main() {
    let number = Some(42);

    // match 守卫:在模式基础上加额外条件
    match number {
        Some(x) if x > 100 => println!("超大数字: {}", x),
        Some(x) if x > 50  => println!("大数字: {}", x),
        Some(x)             => println!("小数字: {}", x),
        None                => println!("没有数字"),
    }

    // if let 语法糖——只关心 Some 的情况
    let value = Some("Rust");
    if let Some(lang) = value {
        println!("语言是: {}", lang);
    }

    // 带 else 的 if let
    let empty: Option<i32> = None;
    if let Some(x) = empty {
        println!("有值: {}", x);
    } else {
        println!("没有值");
    }
}

输出:

TEXT 📖 仅展示
小数字: 42
语言是: Rust
没有值

match 守卫用 if 关键字在模式后添加额外条件——只有模式匹配且条件为真时才会进入该分支。if let 适合"只关心一种模式"的场景。


▶ 示例 4:match 解构枚举与元组(难度 ⭐⭐⭐)

RUST
// ============================================
// match 解构:枚举 + 元组 + 守卫
// ============================================

enum Shape {
    Circle(f64),
    Rectangle(f64, f64),
    Triangle(f64, f64, f64),
}

fn describe_shape(shape: &Shape) -> String {
    match shape {
        Shape::Circle(radius) if *radius > 10.0 => {
            format!("大圆(半径 {:.1})", radius)
        }
        Shape::Circle(radius) => {
            format!("小圆(半径 {:.1})", radius)
        }
        Shape::Rectangle(w, h) if w == h => {
            format!("正方形(边长 {:.1})", w)
        }
        Shape::Rectangle(w, h) => {
            format!("矩形(宽 {:.1},高 {:.1})", w, h)
        }
        Shape::Triangle(a, b, c) => {
            format!("三角形(边 {:.1}, {:.1}, {:.1})", a, b, c)
        }
    }
}

fn main() {
    let shapes = [
        Shape::Circle(5.0),
        Shape::Circle(15.0),
        Shape::Rectangle(4.0, 4.0),
        Shape::Rectangle(3.0, 5.0),
        Shape::Triangle(3.0, 4.0, 5.0),
    ];

    println!("=== 图形描述 ===");
    for shape in &shapes {
        println!("{}", describe_shape(shape));
    }

    println!("\n=== 坐标分类 ===");
    let points: [(i32, i32); 4] = [(0, 0), (3, 0), (0, -2), (5, 7)];
    for point in points {
        let desc = match point {
            (0, 0) => "原点".to_string(),
            (x, 0) => format!("x 轴上 (x={})", x),
            (0, y) => format!("y 轴上 (y={})", y),
            (x, y) if x > 0 && y > 0 => format!("第一象限 ({}, {})", x, y),
            (x, y) => format!("其他象限 ({}, {})", x, y),
        };
        println!("点 {:?}: {}", point, desc);
    }
}

输出:

TEXT 📖 仅展示
=== 图形描述 ===
小圆(半径 5.0)
大圆(半径 15.0)
正方形(边长 4.0)
矩形(宽 3.0,高 5.0)
三角形(边 3.0, 4.0, 5.0)

=== 坐标分类 ===
点 (0, 0): 原点
点 (3, 0): x 轴上 (x=3)
点 (0, -2): y 轴上 (y=-2)
点 (5, 7): 第一象限 (5, 7)

match 的解构能力可以同时匹配枚举变体、提取内部值、加上守卫条件,实现精确的分类逻辑。编译器的穷尽性检查确保你不会遗漏任何情况。


▶ 示例 5:if let 与 while let 实战(难度 ⭐⭐)

RUST
// ============================================
// if let 和 while let:简化 Option/Result 处理
// ============================================

fn divide(a: f64, b: f64) -> Option<f64> {
    if b == 0.0 {
        None
    } else {
        Some(a / b)
    }
}

fn main() {
    println!("=== if let 处理 Option ===");
    let results = [divide(10.0, 2.0), divide(8.0, 0.0), divide(15.0, 3.0)];

    for result in &results {
        if let Some(value) = result {
            println!("除法成功: {:.2}", value);
        } else {
            println!("除法失败: 除数为零");
        }
    }

    println!("\n=== if let 链式处理 ===");
    let alice_input: Option<&str> = Some("42");
    let bob_input: Option<&str> = None;

    if let Some(num_str) = alice_input {
        if let Ok(num) = num_str.parse::<i32>() {
            println!("Alice 的数字: {}", num);
        }
    }

    if let Some(num_str) = bob_input {
        if let Ok(num) = num_str.parse::<i32>() {
            println!("Bob 的数字: {}", num);
        }
    } else {
        println!("Bob 未输入数据");
    }

    println!("\n=== while let 模拟迭代 ===");
    let mut stack = vec![3, 2, 1];
    while let Some(top) = stack.pop() {
        println!("弹出: {}", top);
    }
    println!("栈已空");
}

输出:

TEXT 📖 仅展示
=== if let 处理 Option ===
除法成功: 5.00
除法失败: 除数为零
除法成功: 5.00

=== if let 链式处理 ===
Alice 的数字: 42
Bob 未输入数据

=== while let 模拟迭代 ===
弹出: 1
弹出: 2
弹出: 3
栈已空

if let 适合只关心 Some/Ok 而忽略其他情况的场景;while let 适合循环取出值直到遇到 None 的场景(如 pop() 栈操作)。两者都比完整的 match 更简洁。


❓ 常见问题

Q match 和 switch 有什么区别?
A match 比 switch 强大得多。
Q 为什么 match 穷尽性检查这么重要?
A 因为它是"编译器帮你找 bug"。
Q if let 什么时候用?
A 只关心一种模式时。
Q match 的 _ 通配符和变量名有什么区别?
A _ 完全忽略值且不绑定,变量名会绑定值但编译器可能警告未使用。
Q if 表达式两个分支类型不一致怎么办?
A 编译报错。

📖 小节


📝 作业

  1. 难度 ⭐:写一个函数 fn number_to_word(n: i32) -> &'static str,用 match 把数字 0-5 映射为英文单词(zero/one/two/three/four/five),其他数字返回 "unknown"。
  2. 难度 ⭐⭐:定义枚举 enum TrafficLight { Red, Yellow, Green },用 match 给每种颜色返回对应的"等待时间"(红 30s、黄 3s、绿 45s)。
  3. 难度 ⭐⭐⭐:写一个函数 fn describe_point(point: (i32, i32)),用 match 处理一个二维坐标点:原点 (0,0) 输出 "origin"、在 x 轴上 (x,0) 输出 "on x-axis"、在 y 轴上 (0,y) 输出 "on y-axis"、其余输出坐标值。
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏