一句话总结
Rust 的控制流是表达式导向的:if,match,loop 都能产出值.
其中 match 是核心--它强制穷尽所有分支,能解构数据,是安全使用枚举与 Option 的钥匙.
if let / while let / let...else 则是它针对常见场景的轻量简写.
表达式导向:控制流能返回值
Rust 里 if 是表达式,能直接产出值赋给变量(无需像 Go 那样先声明再在分支里赋值):
|
分支必须返回同一类型,且注意没有分号
各分支产出的值类型必须一致(if 缺 else 时,隐含的"无值分支"是 ()).
另外,作为返回值的表达式末尾不加分号--加了分号就变成语句,值变成 ().这是 Rust 新手极易踩的坑,函数返回值同理.
三种循环
Rust 有 loop(无限循环),while,for.其中 loop 能通过 break 返回值,for 统一遍历迭代器:
|
没有 C 风格 for
Rust 不提供 for (i=0; i<n; i++) 语法.需要索引时用 for i in 0..n,需要元素 + 索引时用 .enumerate()(第 09 篇迭代器会讲).这迫使代码更安全--不会出现越界错误.
match:穷尽的模式匹配
match 拿一个值,逐个比对模式,执行第一个匹配分支.最简单的形态像 switch:
|
但它和 Go switch 有个本质区别:穷尽性检查(exhaustiveness).编译器要求覆盖所有可能,否则报错:
|
Go 的 switch 漏写一个 case 顶多走 default 或啥也不做,bug 静悄悄.
Rust 的 match 像较真的审稿人:没处理的情况当场打回.给枚举新增一个变体时,所有未覆盖的 match 都会编译失败--这是重构时极宝贵的安全网.
match 的真正威力:解构
模式不仅能匹配字面值,还能拆开复合数据,把内部值绑定到变量.这正是处理带数据枚举的方式:
|
一步到位:判断是哪个变体 + 取出它携带的数据.处理 Option 同理,这正是第 06 篇说的"用 match 拆开 Option".
解构与所有权移走
Message::Write(text) 这行:当 msg 不是引用时,解构会移走所有权--text 取得了 String 的所有权,此后不能再使用 msg.
如果只是想借用而非消耗,可以匹配引用:
|
这样 text 自动退化为引用 &String,不会移走数据,match 之后 msg 仍可继续使用.
守卫,范围,绑定,多模式
模式的表达力很丰富,几个高频用法:
|
if let:只关心一种情况时
当只在乎"匹配某一个模式,其余忽略",写完整 match 就显得啰嗦:
|
if let 也能搭配 else.它本质是 match 的语法糖,编译器自动用 _ => () 兜底--穷尽检查并未丢失,只是省去显式写通配分支的繁琐,适合"成功就处理,否则跳过/兜底".
if let 类似 Go 的 if v, ok := m[key]; ok { ... }--同时完成"取值 + 判断是否存在",一行搞定常见情况.
while let 与 let...else
while let 在模式持续匹配时循环,常用于消费容器:
|
let...else 用于"必须成功取值,否则提前返回",能避免层层嵌套,保持主流程平铺:
|
最佳实践:按场景选对工具
- 要处理全部情况,需要穷尽检查 →
match - 只关心一种成功情况 →
if let - 反复取值直到 None →
while let - 取不到就提前返回,让主流程保持平铺 →
let...else
let...else 是减少"嵌套地狱"的利器,尤其适合函数开头的一连串前置校验.
快速回顾
- 表达式导向:
if/match/loop都能返回值;作为返回值的表达式末尾不加分号. - 三种循环:
loop(可 break 带值)/while/for(遍历迭代器,无 C 风格 for). - match 穷尽性:必须覆盖所有分支;新增枚举变体会让旧 match 编译失败,是重构安全网.
- 解构:match 拆开枚举/元组/结构体并绑定内部值,优雅处理带数据枚举与 Option.
- 模式增强:守卫
if,范围1..=9,多模式|,绑定@. - 简写家族:
if let(单一情况),while let(循环取值),let...else(取不到即提前返回).
动手练习
- 范围模式:用
match+ 范围模式,把分数 0-100 映射到等级 A/B/C/D. - 解构枚举:定义
enum Shape { Circle(f64), Rect(f64, f64) },用 match 解构计算面积. - if let 简写:把"仅当为 Circle 时打印半径"用
if let实现. - while let 消费:用
while let配合Vec::pop逆序打印一个向量. - let...else 校验:用
let...else写一个函数--解析字符串为数字,失败则提前返回默认值,成功则继续主流程.