本节摘要:讲清 Rust"表达式优先"的语言性格——赋值语句之外的多数语法块都产生值;演示 if 作表达式、loop 的 break 返回值、match 的穷尽性检查。读完你能写出无多余中间变量的紧凑流程代码。
fn fine(amount: u32, reason: &str) -> u32 { println!("罚款 {},案由:{}", amount, reason); amount * 2 // 无分号:这是返回表达式 }
最后一行不带分号即为返回值;带分号就变成语句,返回值退化为单元值——新手高频案件" expected u32, found()" 多半是这枚分号。提前返回用 return。
let severity = if loss > 1000 { "重大" } else { "一般" };
if 的两个分支必须同类型,编译器把这个一致性也管起来。三元运算符因此不存在——if 表达式已覆盖。
三种循环各有判决书:
let mut n = 0; let result = loop { // loop 的 break 可以带值返回 n += 1; if n == 10 { break n * 2; } }; for i in 1..=5 { print!("{} ", i); } // 含终值区间 for b in [1, 2, 3] { print!("{} ", b); } // 遍历数组,越界不可能
loop 配 break 带值,配合循环标签可从嵌套深处直接结案:'outer: loop { ... break 'outer x; }。
enum Verdict { Guilty, Innocent, Mistrial } fn penalty(v: Verdict) -> u32 { match v { Verdict::Guilty => 100, Verdict::Innocent => 0, Verdict::Mistrial => 30, } }
少写一个分支,编译器报 non-exhaustive patterns,并贴心列出漏掉的变体。这与 C 的 switch(漏 case 静默通过)是制度级差异——枚举第 4 章展开,这里先记住:match 必须接住每一种可能,_ 是兜底通配。

Rust 的控制流几乎都是表达式,loop 也不例外——这使它天然适合"重试直到成功"的模式,且不需要外部可变标志位。
fn next_case_id(counter: &mut u32) -> u32 { loop { *counter += 1; if *counter % 7 != 0 { // 跳过不吉利编号只是业务规则的替身 break *counter; // break 带值:loop 的产出 } } } fn main() { let mut c = 6; let id = next_case_id(&mut c); // 得 8:7 被跳过 println!("受理编号 {}", id); }
同一家族还有 while let 与 if let,两者都是 match 的单项速裁版。选择的原则很简单:只有一种关心的形态用 if let,两种以上或需要穷尽性保证时上 match——match 的穷尽检查是编译器免费送的审查员,漏掉的形态会在编译期点名。
嵌套循环里 break 默认只退出一层,处理二维检索这类案件容易退错层。标签把退出目标写成显式名字。
fn find_pair(grid: &[[u8; 3]; 3], target: u8) -> (usize, usize) { let mut found = (0, 0); 'outer: for (r, row) in grid.iter().enumerate() { for (c, cell) in row.iter().enumerate() { if *cell == target { found = (r, c); break 'outer; // 一次退到外层,不再扫余下各行 } } } found }
对比"用标志变量逐层判断退出"的传统写法,标签少两个可变状态,控制流意图也更直白。continue 同样可以带标签跳到指定层下一轮。
| 手段 | 本质 | 何时选用 |
|---|---|---|
| if / else | 表达式判分支 | 二三路、条件彼此独立 |
| match | 穷尽模式分派 | 枚举形态、范围与守卫 |
| if let / while let | 单形态速裁 | 只关心一种可能 |
| loop / while | 循环,loop 可带值产出 | 重试、状态机推进 |
| ? 运算符 | 错误短路上抛 | 第 6 章专庭审理 |
返回值风格也顺带立个规矩:能用表达式收尾的函数(最后一句就是值)不必写 return,return 只用于提前退庭。全文统一这一条,代码的呼吸感会明显一致。
enum Filing { Open, Closed(u32) } fn status(f: &Filing) -> &'static str { match f { Filing::Open => "进行中", // 漏掉 Closed:E0004 non-exhaustive patterns } .to_string() .leak() // 占位,勿学 }
上面示例故意保留两处问题:E0004 的修复是补全分支而非加 _(枚举新增形态时 _ 会静默吞掉新形态);leak 写法是坏味道示范,返回 String 才是正解。match 的价值恰恰在编译器替你追形态:枚举演进时,所有漏改的 match 会同时点名,这是重构安全网的重要一环。
把同一段"找最大值"逻辑分别用 loop+break、while+索引、迭代器 max_by 三种写法各写一遍,对比行数与可变状态数量。典型结论:loop 版两个可变变量,while 版一个外加边界判断,迭代器版零可变状态。这个练习建立的手感是后续章节反复引用的——第 4 章集合与第 6 章 Result 组合子,都是"用表达式与组合子消灭可变状态"这条主线的延续。