Rust if let 和 while let
你已经用 match 处理过 Option 和 Result。不过大多数时候,你真正关心的其实只有一种模式,其余的都想忽略。为了什么都不做而写一个完整的 match,再加上一个 _ => {} 分支,也能用,但对这种场景来说未免太隆重了。if let 就是为此准备的捷径。
在这里用完整 match 的问题
Section titled “在这里用完整 match 的问题”fn main() { let age: Option<i32> = Some(30);
match age { Some(value) => println!("年龄是 {value}"), _ => {}, // 其实我们并不关心这种情况 }}那个 _ => {} 什么也没做,只是为了满足 match“每种情况都必须处理”的规则。如果你只想在 Some 时做点事,这段代码就比它要表达的意思多出了不少。
if let
Section titled “if let”fn main() { let age: Option<i32> = Some(30);
if let Some(value) = age { println!("年龄是 {value}"); }}输出:
年龄是 30可以这样读:“如果 age 能匹配模式 Some(value),就执行这个代码块,value 绑定到里面的值。”如果 age 是 None,这个代码块就直接跳过——不报错,也不打印任何东西。
加上 else
Section titled “加上 else”if let 也可以带 else,和普通的 if 一样:
fn main() { let age: Option<i32> = None;
if let Some(value) = age { println!("年龄是 {value}"); } else { println!("没有提供年龄"); }}用 if let 处理 Result
Section titled “用 if let 处理 Result”对 Result 也是同样的用法,你在 Result 那一页已经见过它了:
fn main() { let input = "42";
if let Ok(number) = input.parse::<i32>() { println!("解析结果:{number}"); } else { println!("无法解析"); }}用 else if let 串起来
Section titled “用 else if let 串起来”就像 if 有 else if 一样,if let 后面也可以接 else if let。Rust 会依次尝试每个模式,执行第一个匹配成功的代码块。如果你要按优先顺序在几个地方找一个值,这种写法就很方便:
fn main() { let nickname: Option<&str> = None; let full_name: Option<&str> = Some("Ferris Crab");
if let Some(nick) = nickname { println!("嗨,{nick}!"); } else if let Some(name) = full_name { println!("你好,{name}。"); } else { println!("你好,陌生人。"); }}输出:
你好,Ferris Crab。nickname 是 None,所以第一个代码块被跳过。full_name 匹配 Some(name),于是执行这个代码块,最后的 else 根本不会走到。你还可以在这条链的任意位置混入普通条件,比如 else if is_admin { ... }。
每个变量只在自己的代码块里有效:nick 不能在第二个代码块里使用,name 在整条链结束后也用不了。如果后面的代码都需要用到拆出来的值,可以看看模式匹配那一页的 let-else。
while let
Section titled “while let”while let 把同样的思路用在循环上:只要值还能匹配模式,就一直循环下去。用来把一个集合逐个取空时特别顺手:
fn main() { let mut stack = vec![1, 2, 3];
while let Some(top) = stack.pop() { println!("{top}"); }}输出:
321只要还有元素,.pop() 就返回 Some(value);vector 空了之后就返回 None——这时 while let 会自动结束循环,不需要你手动检查长度。
if let 和 match,该用哪个
Section titled “if let 和 match,该用哪个”如果真的需要分别处理多种情况,就用 match。如果你只关心一种模式,其余的都可以直接忽略,就用 if let。它并不是用来取代 match 的,只是“我只关心一个分支”这种常见情况的简短写法。
1. 把栈里的数加起来
Section titled “1. 把栈里的数加起来”从 let mut numbers = vec![4, 8, 15]; 和一个初始为 0 的累加变量 total 开始。用 while let 配合 .pop(),每次从末尾取出一个数,加到 total 上,并打印取出的数和新的总和。提示:total 需要 mut,total += n 就是把 n 加上去。
查看答案
fn main() { let mut numbers = vec![4, 8, 15]; let mut total = 0;
while let Some(n) = numbers.pop() { total += n; println!("取出 {n},现在总和是 {total}"); }}输出:
取出 15,现在总和是 15取出 8,现在总和是 23取出 4,现在总和是 27.pop() 是从末尾取元素的,所以 15 最先出来。
2. 检查一段文本里的年龄
Section titled “2. 检查一段文本里的年龄”写下 let input = "17";。如果它能解析成 u32,就打印这个年龄是成年人(18 岁及以上)还是未成年人;如果解析不了,就打印它不是数字。也试试把 input 改成 "abc"。提示:在 if let 代码块里面再写一个普通的 if / else。
查看答案
fn main() { let input = "17";
if let Ok(age) = input.parse::<u32>() { if age >= 18 { println!("{age} 岁是成年人"); } else { println!("{age} 岁是未成年人"); } } else { println!("“{input}”不是数字"); }}输出:
17 岁是未成年人如果 input = "abc",解析会失败,于是执行外层的 else,打印 “abc”不是数字。
if let Pattern = value { ... }只在value匹配Pattern时执行代码块,其他情况都会悄悄跳过。- 加上
else就能处理不匹配的情况,和普通的if一样;还可以用else if let把几个模式串起来。 while let Pattern = value { ... }只要值还能匹配就一直循环,很适合配合.pop()把集合取空。- 只关心一种模式时用
if let/while let;需要分别处理好几种情况时用match。
小测验
1. 运行 if let Some(value) = age 时,如果 age 是 None,会发生什么?
2. 什么时候应该用 match 而不是 if let?
3. 在 while let Some(top) = stack.pop() { ... } 中,循环什么时候停止?
得分: 0 / 3
- if let 和 let else 简洁控制流——《Rust 程序设计语言》:官方的入门章节,也讲到了 let-else。
- if let——《通过例子学 Rust》:用
Option和自定义枚举写的可运行示例,还解释了只关心一种模式时,为什么if let比match更好。 - while let——《通过例子学 Rust》:把一个
loop+match的循环改写成更短的while let。 if letpatterns — The Rust Reference(英文):准确的规则,包括if let和match之间的关系。