Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help


x-i18n: generated_at: “2026-03-01T13:49:57Z” model: gemini-3-flash-preview provider: google-gemini-cli source_hash: f6fcaea8b33c8a39aedc4209b7b8bb57a389ab8c64ff051a1793f0c0e025e1da source_path: ch06-03-if-let.md workflow: 16

使用 if letlet...else 的简洁控制流 (Concise Control Flow with if let and let...else)

Concise Control Flow with if let and let...else

if let 语法让你能以一种更简洁的方式组合 iflet,来处理与一个模式相匹配的值,同时忽略其余的值。考虑示例 6-6 中的程序,它匹配 config_max 变量中的 Option<u8> 值,但只想在该值为 Some 变体时执行代码。

The if let syntax lets you combine if and let into a less verbose way to handle values that match one pattern while ignoring the rest. Consider the program in Listing 6-6 that matches on an Option<u8> value in the config_max variable but only wants to execute code if the value is the Some variant.

#![allow(unused)]
fn main() {
{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-06/src/main.rs:here}}
}

如果该值是 Some,我们通过将该值绑定到模式中的变量 max 来打印 Some 变体中的值。我们不想对 None 值做任何事情。为了满足 match 表达式,我们必须在仅处理一个变体后添加 _ => (),这是添加起来令人讨厌的样板代码。

If the value is Some, we print out the value in the Some variant by binding the value to the variable max in the pattern. We don’t want to do anything with the None value. To satisfy the match expression, we have to add _ => () after processing just one variant, which is annoying boilerplate code to add.

相反,我们可以使用 if let 以一种更短的方式来编写。以下代码的行为与示例 6-6 中的 match 相同:

Instead, we could write this in a shorter way using if let. The following code behaves the same as the match in Listing 6-6:

#![allow(unused)]
fn main() {
{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-12-if-let/src/main.rs:here}}
}

if let 语法接收一个模式和一个表达式,两者用等号分隔。它的工作方式与 match 相同,其中表达式提供给 match,模式是它的第一个分支。在这种情况下,模式是 Some(max),且 max 绑定到 Some 内部的值。然后我们可以在 if let 块的主体中使用 max,就像我们在对应的 match 分支中使用 max 一样。if let 块中的代码仅在该值匹配模式时运行。

The syntax if let takes a pattern and an expression separated by an equal sign. It works the same way as a match, where the expression is given to the match and the pattern is its first arm. In this case, the pattern is Some(max), and the max binds to the value inside the Some. We can then use max in the body of the if let block in the same way we used max in the corresponding match arm. The code in the if let block only runs if the value matches the pattern.

使用 if let 意味着更少的输入、更少的缩进和更少的样板代码。然而,你失去了 match 强制执行的穷尽性检查,该检查能确保你没有忘记处理任何情况。在 matchif let 之间做出选择取决于你在特定情况下的操作,以及获得简洁性是否是失去穷尽性检查的合适折衷。

Using if let means less typing, less indentation, and less boilerplate code. However, you lose the exhaustive checking match enforces that ensures that you aren’t forgetting to handle any cases. Choosing between match and if let depends on what you’re doing in your particular situation and whether gaining conciseness is an appropriate trade-off for losing exhaustive checking.

换句话说,你可以将 if let 看作是 match 的语法糖,它在值匹配一个模式时运行代码,然后忽略所有其他值。

In other words, you can think of if let as syntax sugar for a match that runs code when the value matches one pattern and then ignores all other values.

我们可以给 if let 包含一个 else。与 else 配合的代码块与在等效于 if letelsematch 表达式中与 _ 情况配合的代码块相同。回想示例 6-4 中的 Coin 枚举定义,其中 Quarter 变体还持有一个 UsState 值。如果我们想计算看到的除了 25 美分以外的所有硬币,同时宣布 25 美分所属的州,我们可以使用 match 表达式来做到这一点,如下所示:

We can include an else with an if let. The block of code that goes with the else is the same as the block of code that would go with the _ case in the match expression that is equivalent to the if let and else. Recall the Coin enum definition in Listing 6-4, where the Quarter variant also held a UsState value. If we wanted to count all non-quarter coins we see while also announcing the state of the quarters, we could do that with a match expression, like this:

#![allow(unused)]
fn main() {
{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-13-count-and-announce-match/src/main.rs:here}}
}

或者我们可以使用 if letelse 表达式,如下所示:

Or we could use an if let and else expression, like this:

#![allow(unused)]
fn main() {
{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/no-listing-14-count-and-announce-if-let-else/src/main.rs:here}}
}

使用 let...else 保持在“快乐路径”上 (Staying on the “Happy Path” with let...else)

Staying on the “Happy Path” with let...else

常见的模式是在存在值时执行某些计算,否则返回一个默认值。继续以带有 UsState 值的硬币为例,如果我们想根据 25 美分硬币上州的年龄说一些有趣的话,我们可能会在 UsState 上引入一个方法来检查州的年龄,如下所示:

The common pattern is to perform some computation when a value is present and return a default value otherwise. Continuing with our example of coins with a UsState value, if we wanted to say something funny depending on how old the state on the quarter was, we might introduce a method on UsState to check the age of a state, like so:

#![allow(unused)]
fn main() {
{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-07/src/main.rs:state}}
}

然后,我们可能会使用 if let 来匹配硬币类型,在条件主体中引入 state 变量,如示例 6-7 所示。

Then, we might use if let to match on the type of coin, introducing a state variable within the body of the condition, as in Listing 6-7.

#![allow(unused)]
fn main() {
{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-07/src/main.rs:describe}}
}

这可以完成任务,但它将工作推到了 if let 语句的主体中,如果要做的工作更复杂,可能很难看清顶层分支是如何关联的。我们也可以利用表达式产生一个值的事实,要么从 if let 产生 state,要么提前返回,如示例 6-8 所示。(你也可以用 match 做类似的事情。)

That gets the job done, but it has pushed the work into the body of the if let statement, and if the work to be done is more complicated, it might be hard to follow exactly how the top-level branches relate. We could also take advantage of the fact that expressions produce a value either to produce the state from the if let or to return early, as in Listing 6-8. (You could do something similar with a match, too.)

#![allow(unused)]
fn main() {
{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-08/src/main.rs:describe}}
}

不过,这种方式本身也有点烦人!if let 的一个分支产生一个值,而另一个分支则完全从函数返回。

This is a bit annoying to follow in its own way, though! One branch of the if let produces a value, and the other one returns from the function entirely.

为了让这种常见模式更优雅地表达,Rust 提供了 let...elselet...else 语法左侧接收一个模式,右侧接收一个表达式,这与 if let 非常相似,但它没有 if 分支,只有 else 分支。如果模式匹配,它将在外部作用域中绑定来自模式的值。如果模式“不”匹配,程序将流向 else 分支,该分支必须从函数返回。

To make this common pattern nicer to express, Rust has let...else. The let...else syntax takes a pattern on the left side and an expression on the right, very similar to if let, but it does not have an if branch, only an else branch. If the pattern matches, it will bind the value from the pattern in the outer scope. If the pattern does not match, the program will flow into the else arm, which must return from the function.

在示例 6-9 中,你可以看到在使用 let...else 代替 if let 时,示例 6-8 看起来是什么样子。

In Listing 6-9, you can see how Listing 6-8 looks when using let...else in place of if let.

#![allow(unused)]
fn main() {
{{#rustdoc_include ../listings/ch06-enums-and-pattern-matching/listing-06-09/src/main.rs:describe}}
}

请注意,通过这种方式,函数的主体保持在“快乐路径”上,而不会像 if let 那样为两个分支提供明显不同的控制流。

Notice that it stays on the “happy path” in the main body of the function this way, without having significantly different control flow for two branches the way the if let did.

如果你遇到程序逻辑过于冗长而无法使用 match 表达的情况,请记住 if letlet...else 也在你的 Rust 工具箱中。

If you have a situation in which your program has logic that is too verbose to express using a match, remember that if let and let...else are in your Rust toolbox as well.

总结 (Summary)

Summary

我们现在已经涵盖了如何使用枚举来创建可以是枚举值集合之一的自定义类型。我们已经展示了标准库的 Option<T> 类型如何帮助你利用类型系统来防止错误。当枚举值内部持有数据时,你可以根据需要处理的情况数量,使用 matchif let 来提取并使用这些值。

We’ve now covered how to use enums to create custom types that can be one of a set of enumerated values. We’ve shown how the standard library’s Option<T> type helps you use the type system to prevent errors. When enum values have data inside them, you can use match or if let to extract and use those values, depending on how many cases you need to handle.

你的 Rust 程序现在可以使用结构体和枚举来表达你领域中的概念。创建要在 API 中使用的自定义类型可确保类型安全:编译器将确保你的函数仅获得每个函数预期的类型值。

Your Rust programs can now express concepts in your domain using structs and enums. Creating custom types to use in your API ensures type safety: The compiler will make certain your functions only get values of the type each function expects.

为了向用户提供一个组织良好、易于使用且仅公开用户真正需要的 API,现在让我们转向 Rust 的模块系统。

In order to provide a well-organized API to your users that is straightforward to use and only exposes exactly what your users will need, let’s now turn to Rust’s modules.