mirror of
https://github.com/gnu4cn/rust-lang-zh_CN.git
synced 2026-08-19 04:33:27 +08:00
Updated src/enums_and_pattern_matching/if-let_control_flow.md'.
This commit is contained in:
6
projects/if-let_demo/Cargo.toml
Normal file
6
projects/if-let_demo/Cargo.toml
Normal file
@@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "if-let_demo"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
13
projects/if-let_demo/src/main.rs
Normal file
13
projects/if-let_demo/src/main.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
fn main() {
|
||||
let config_max = Some(3u8);
|
||||
|
||||
match config_max {
|
||||
Some(max) => println! ("The maximum is configured to be {max}"),
|
||||
_ => ()
|
||||
}
|
||||
|
||||
if let Some(max) = config_max {
|
||||
println! ("The maximum is configured to be {max}");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,80 +1,81 @@
|
||||
# 使用 `if let` 与 `let else` 的简明控制流
|
||||
# `if let` 与 `let else` 下的简明控制流
|
||||
|
||||
**Concise Control Flow with `if let`**
|
||||
|
||||
|
||||
`if let` 这种语法,可让咱们将 `if` 和 `let` 结合起来,以一种不那么冗长的方式,处理与一种模式匹配的值,而忽略其他值。请看下面清单 6-6 中,会匹配 `config_max` 变量中的一个 `Option<u8>` 值,但只在该值为 `Some` 变体时,才打算执行代码的程序。
|
||||
`if let` 语法允许咱们将 `if` 和 `let` 结合为一种不太冗长的方式来处理匹配一种模式匹配的值,同时忽略其余值。考虑下面清单 6-6 中的程序,其匹配 `config_max` 变量中的 `Option<u8>` 值,但只打算在该值为 `Some` 变体时执行代码。
|
||||
|
||||
|
||||
```rust
|
||||
let config_max = Some(3u8);
|
||||
|
||||
match config_max {
|
||||
Some(max) => println! ("The maximum is configured to be {}", max),
|
||||
Some(max) => println! ("The maximum is configured to be {max}"),
|
||||
_ => ()
|
||||
}
|
||||
```
|
||||
|
||||
*清单 6-6:只关心值为 `Some` 时执行代码的 `match` 表达式*
|
||||
<a name="listing_6-6"></a>
|
||||
**清单 6-6**:只关心在值为 `Some` 时执行代码的 `match` 表达式
|
||||
|
||||
|
||||
如果值为 `Some`,我们会将该值绑定到模式中的变量 `max`,打印出 `Some` 变种中的值。我们不打算对 `None` 值,做任何处理。为了满足 `match` 表达式的要求,我们必须在仅处理一个变种后,加上 `_ => ()`,而这是很烦人的样板代码。
|
||||
当值为 `Some` 时,我们通过绑定值到模式中的变量 `max` 打印出 `Some` 变种中的值。我们不打算对 `None` 值做任何事情。为了满足 `match` 表达式(穷尽要求),我们不得不在仅处理一个变种后就添加 `_ => ()`,而这属于令人讨厌的样板代码。
|
||||
|
||||
相反,我们可以使用 `if let`,以更简短的方式编写这段代码。以下代码的行为,与清单 6-6 中的 `match` 表达式相同:
|
||||
相反,我们可以使用 `if let` 以更简短的方式编写这段代码。以下代码与清单 6-6 中 `match` 表达式行为相同:
|
||||
|
||||
|
||||
```rust
|
||||
let config_max = Some(3u8);
|
||||
|
||||
if let Some(max) = config_max {
|
||||
println! ("The maximum is configured to be {}", max);
|
||||
println! ("The maximum is configured to be {max}");
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
语法 `if let` 取以等号分隔的一个模式和表达式。其工作原理与 `match` 表达式相同,`if let` 中的表达式,会被给到 `match`,而其中的模式,便是 `match` 的首个支臂。在本例中,模式为 `Some(max)`,`max` 会绑定大 `Some` 中的值。我们随后便可以在 `if let` 代码块的正文中,以咱们曾在对应的 `match` 支臂中使用 `max` 的同样方式,使用 `max` 了。如果值与模式不匹配,这个 `if let` 代码块中的代码就不会运行。
|
||||
语法 `if let` 取以等号分隔的一个模式与一个表达式。其工作方式与 `match` 表达式相同,其中表达式被提供给 `match` 而模式为他的第一个支臂。在本例中,模式为 `Some(max)`,而 `max` 绑定到 `Some` 内的值。然后,我们可在 `if let` 代码块的主体中使用 `max`,以咱们在对应 `match` 支臂中使用 `max` 的同样方式。`if let` 代码块中的代码只会在值与模式匹配时运行。
|
||||
|
||||
|
||||
> **译注**:`if let` 实际上是两部分,其中 `let Some(max) = config_max` 是个检验表达式 scrutinee expression。后面会看到类似的 `while let` 语法。
|
||||
> **译注**:
|
||||
>
|
||||
> - `if let` 实际上是两部分,其中 `let Some(max) = config_max` 是个检验表达式,scrutinee expression。后面会看到类似的 `while let` 语法;
|
||||
>
|
||||
> - 按照 Erlang/OTP 下的思路,这里的 `=` 是个模式匹配运算符,而不是赋值。
|
||||
|
||||
|
||||
使用 `if let`,意味着更少输入、更少缩进和更少模板代码。但是,咱们会失去 `match` 表达式所带来的穷尽检查。在 `match` 和 `if let` 之间做出选择,取决于咱们在咱们的特定情况下,正在做什么;以及在失去穷尽检查的同时,是否能获得了简洁性。
|
||||
使用 `if let` 意味着更少的键入、更少的缩进及更少的样板代码。但是,咱们将失去 `match` 表达式强制执行的穷尽检查,其确保咱们没有忘记处理任何情况。在 `match` 和 `if let` 之间选择取决于咱们在特定情况下正在做什么,以及是否认为获得简洁性是值得牺牲穷尽检查的合理权衡。
|
||||
|
||||
换句话说,咱们可以把 `if let`,视为在值与一种模式匹配时执行代码,并在随后忽略所有其他值的 `match` 表达式的语法糖。
|
||||
|
||||
我们可以在 `if let` 中加入一个 `else`。与 `else` 搭配的代码块,与等同于这个 `if let` 与 `else` 的 `match` 表达式中,`_` 情况下的代码块相同。回想清单 6-4 中的 `Coin` 枚举定义,其中 `Quarter` 变种还包含了一个 `UsState` 值。如果我们想清点我们见到的所有非 25 美分硬币,同时还要公布 25 美分硬币的州份,我们本可以使用一个 `match` 表达式来完成这点,就像下面这样:
|
||||
换句话说,咱们可把 `if let` 视为 `match` 表达式的语法糖,当值与一种模式匹配时运行代码,然后忽略所有别的值。
|
||||
|
||||
我们可在 `if let` 下包含 `else`。`else` 下的代码块与等同于这个 `if let` 与 `else` 的 `match` 表达式中,`_` 情况下的代码块相同。回顾 [清单 6-4](./match_control_flow.md#listing_6-4) 中 `Coin` 枚举的定义,其中 `Quarter` 变种还保存了个 `UsState` 值。若我们打算清点我们看到的所有非 25 美分硬币,同时还要喊出 25 美分硬币的州份,我们本可以一个 `match` 表达式实现,就像下面这样:
|
||||
|
||||
```rust
|
||||
let mut count = 0;
|
||||
let mut count = 0;
|
||||
|
||||
match coin {
|
||||
Coin::Quarter(state) => println! ("State quarter from {:?}!", state),
|
||||
_ => count += 1,
|
||||
}
|
||||
match coin {
|
||||
Coin::Quarter(state) => println! ("State quarter from {state:?}!"),
|
||||
_ => count += 1,
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
或者,我们可以使用 `if let` 和 `else` 表达式,就像这样:
|
||||
或者我们可以使用 `if let` 与 `else` 表达式,就像这样:
|
||||
|
||||
|
||||
```rust
|
||||
let mut count = 0;
|
||||
let mut count = 0;
|
||||
|
||||
if let Coin::Quarter(state) = coin {
|
||||
println! ("State quarter from {:?}!", state);
|
||||
} else {
|
||||
count += 1;
|
||||
}
|
||||
if let Coin::Quarter(state) = coin {
|
||||
println! ("State quarter from {state:?}!");
|
||||
} else {
|
||||
count += 1;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## 使用 `let...else` 继续 “快乐小道”
|
||||
## 在 `let...else` 下留在 “快乐小道”
|
||||
|
||||
**Staying on the "Happy Path" with `let...else`**
|
||||
|
||||
|
||||
一种常见的模式,是当某个值存在时执行某些计算,否则返回一个默认值。继续以带有 `UsState` 值的硬币为例,若我们打算根据 25 美分硬币上州份有多少年历史,而说些有趣的话,我们可以在 `UsState` 上引入一个检查该州有多少年历史的方法,就像这样:
|
||||
常见模式是当值存在时执行一些计算,否则返回默认值。继续我们的带有 `UsState` 值的硬币示例,若我们打算根据 25 美分硬币上的州份年代说些有趣的话,我们就可能在 `UsState` 上引入一个方法来检查州份的年代,就像这样:
|
||||
|
||||
|
||||
```rust
|
||||
@@ -89,7 +90,7 @@ impl UsState {
|
||||
}
|
||||
```
|
||||
|
||||
然后,通过在条件主体中引入一个 `state` 变量,我们就可以使用 `if let`,匹配该硬币的类型,如清单 6-7 所示。
|
||||
然后,我们可以使用 `if let` 来匹配硬币的类型,在条件的主体中引入一个 `state` 变量,如下面清单 6-7 中所示。
|
||||
|
||||
文件名:`src/main.rs`
|
||||
|
||||
@@ -107,10 +108,10 @@ fn describe_state_quarter(coin: Coin) -> Option<String> {
|
||||
}
|
||||
```
|
||||
|
||||
*清单 6-7:通过使用嵌套在 `if let` 中的条件,检查某个州在 1900 时是否存在*
|
||||
<a name="listing_6-7"></a>
|
||||
**清单 6-7**:使用嵌套在 `if let` 中的条件,检查某个州在 1900 时是否存在
|
||||
|
||||
|
||||
这样虽然完成了工作,但却把该项工作推到了那个 `if let` 语句的主体中,而如果要完成的工作比较复杂,可能就很难准确地理解,顶层分支之间的关系。我们还可以利用表达式会产生一个值,从 `if let` 中要么产生 `state`,或提前返回这一事实,如清单 6-8 所示。(咱们也可以对 `match` 做类似的事情。)
|
||||
这样就完成了工作,但他已将工作推入了 `if let` 语句的主体中,当要完成的工作更复杂时,就可能很难准确理解顶级分支之间的关系。我们还可以利用表达式会产生值这一事实,要么从 `if let` 产生 `state` 要么提前返回,如清单 6-8 中所示。(在 `match` 下咱们也可实现类似效果。)
|
||||
|
||||
|
||||
文件名:`src/main.rs`
|
||||
@@ -131,16 +132,15 @@ fn describe_state_quarter(coin: Coin) -> Option<String> {
|
||||
}
|
||||
```
|
||||
|
||||
*清单 6-8:使用 `if let` 产生一个值,或提前返回*
|
||||
<a name="listing_6-8"></a>
|
||||
**清单 6-8**:使用 `if let` 产生值,或提前返回*
|
||||
|
||||
|
||||
不过,这样做本身就有点恼人!`if let` 的一个分支会产生了一个值,而另一分支则会从整个函数返回。
|
||||
不过,这样做本身就有点烦人!`if let` 的一个分支会产生值,而另一分支会整个地从函数返回。
|
||||
|
||||
为了使这种常见模式更易于表达,Rust 提供了 `let...else`。`let...else` 语法取左侧的一个模式和右侧的一个表达式,与 `if let` 非常相似,但他没有 `if` 分支,而只有 `else` 分支。当模式匹配时,他将在外层作用域中绑定模式中的值。当模式 *不* 匹配时,程序将流入 `else` 支臂,其必须从函数返回。
|
||||
|
||||
为更好地表达这种常见模式,Rust 提供了 `let...else`。`let...else` 语法的左侧是个模式,右侧是个表达式,与 `if let` 非常相似,但他没有 `if` 分支,只有 `else` 分支。在模式匹配时,他将在外层作用域中,绑定模式中的值。在模式 *不* 匹配时,程序将流入 `else` 支臂,而该支臂必须从函数中返回。
|
||||
|
||||
在清单 6-9 中,咱们可以看到在 `if let` 处 使用 `let...else` 时清单 6-8 效果。请注意,这种方法在该函数主体中,保持了 “快乐路径”,而没有 `if let` 那样,两个分支的控制流明显不同。
|
||||
|
||||
在下面清单 6-9 中,咱们会看到在 `if let` 处使用 `let...else` 时清单 6-8 的样子。
|
||||
|
||||
文件名:`src/main.rs`
|
||||
|
||||
@@ -158,21 +158,22 @@ fn describe_state_quarter(coin: Coin) -> Option<String> {
|
||||
}
|
||||
```
|
||||
|
||||
*清单 6-9:使用 `let...else` 明确该函数的流程*
|
||||
<a name="listing_6-9"></a>
|
||||
**清单 6-9**:使用 `let...else` 来明确函数的流程
|
||||
|
||||
请注意,流程以这种方式保持在函数主体中的 “快乐路径” 上,而没有 `if let` 所做的那样,针对两个分支有着明显不同的控制流。
|
||||
|
||||
如果咱们遇到程序中的逻辑过于冗长,而无法使用 `match` 表达的情形,那么请记住,`if let` 与 `let...else` 也是 Rust 工具箱中的工具。
|
||||
|
||||
当咱们遇到咱们的程序有着过于冗长的逻辑,无法使用 `match` 表达的情况时,那么请记住 `if let` 及 `let...else` 也在咱们的 Rust 工具箱中。
|
||||
|
||||
|
||||
# 本章小结
|
||||
|
||||
|
||||
现在我们已经介绍了如何使用枚举来创建出,可以是一组枚举值中的一个的自定义类型。我们已经展示了标准库的 `Option<T>` 类型,如何帮助咱们使用类型系统,来防止错误。当枚举值中包含数据时,根据咱们需要处理多少种情况,咱们可以使用 `match` 或 `if let`,来提取和使用这些值。
|
||||
我们现在已经介绍了如何使用枚举来创建可以是一组枚举值之一的自定义类型。我们已经展示了标准库的 `Option<T>` 类型如何帮助咱们使用类型系统来防止错误。当枚举值内包含数据时,咱们可使用 `match` 或 `if let` 来提取并使用这些值,具体取决于咱们需要处理多少的情况。
|
||||
|
||||
咱们的 Rust 程序现在可以使用结构体和枚举,来表达咱们领域中的概念了。在咱们的 API 中创建一些自定义类型,确保了类型安全:编译器将确保咱们的函数,只获取到每个他们所期望类型的值。
|
||||
咱们的 Rust 程序现在可以使用结构体和枚举来表达咱们领域中的概念。创建在咱们 API 中使用的自定义类型可确保类型安全:编译器将确保咱们的函数,只会得到各个函数期望类型的值。
|
||||
|
||||
为了向咱们的用户,提供组织良好、简单易用的 API,并且只暴露出咱们用户所需的内容,我们现在来看看 Rust 的模组。
|
||||
为了向咱们的用户提供组织良好、简单易用,且仅暴露出咱们用户所需的 API,我们现在来看看 Rust 的模组。
|
||||
|
||||
|
||||
(End)
|
||||
|
||||
Reference in New Issue
Block a user