Updated src/programming_concepts/control_flow.md'.

This commit is contained in:
Hector PENG
2026-03-08 16:21:50 +08:00
parent 6fbde056e6
commit aaa6464990
3 changed files with 62 additions and 43 deletions

View File

@@ -1,5 +1,7 @@
fn main() {
loop {
println! ("again!");
for number in (1..4).rev() {
println! ("{}!", number);
}
println! ("LIFTOFF!!");
}

View File

@@ -160,7 +160,7 @@ pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str>{
*清单 12-17遍历 `contents` 中的各行*
`lines` 方法会返回一个迭代器。我们将在 [第 13 章](../functional_features/iterators.md) 中,详细讨论迭代器,但请回想一下,咱们在 [清单 3-5](../programming_concepts/control_flow.md#listing-3-5) 中就已见过这种使用迭代器的方法,当时我们与迭代器一起使用了 `for` 循环,对集合中的各个元素执行一些代码。
`lines` 方法会返回一个迭代器。我们将在 [第 13 章](../functional_features/iterators.md) 中,详细讨论迭代器,但请回想一下,咱们在 [清单 3-5](../programming_concepts/control_flow.md#listing_3-5) 中就已见过这种使用迭代器的方法,当时我们与迭代器一起使用了 `for` 循环,对集合中的各个元素执行一些代码。
### 在各行中检索查询字符串

View File

@@ -287,8 +287,7 @@ again!
### 从循环返回值
`loop` 的用途之一,是重试咱们已知可能会失败的某项操作,例如检查某个线程是否已完成其作业。咱们可能还需要将该操作的结果,从循环中传递给咱们代码的其余部分。为此,咱们可以将这个咱们打算返回的值,添加在咱们用于停止循环的 `break` 表达式之后;该值将从循环中返回,以便咱们使用,如下所示:
`loop` 的用途之一,是重试咱们知道可能失败的某项操作,例如检查某个线程是否已完成其作业。咱们可能还需要从循环传递出该操作的结果,到咱们代码的其余部分。要完成这一目的,咱们可将咱们打算返回的值,添加在咱们用于停止循环的 `break` 表达式后;该值将从循环中返回出来,以便咱们使用他,如下所示:
```rust
@@ -307,13 +306,38 @@ fn main() {
}
```
循环前,我们声明一个名为 `counter` 的变量,并将其初始化为 `0`。 然后咱们声明一个名为 `result` 的变量,用于保存该循环返回的值。在循环的每一次迭代,我们都会给那个 `counter` 变量加 `1`,然后检查这个 `counter` 是否等于 `10`。当等于 `10` 时,我们使用带有 `counter * 2` 值的 `break` 关键字。在这个循环结束后,我们使用了一个分号结束那条把该值赋给 `result` 的语句。最后,我们打印 `result` 中的值,本例中`20`
其中的循环前,我们声明一个名为 `counter` 的变量初始化`0`。 然后咱们声明一个名为 `result` 的变量保存该循环返回的值。在循环的每一次迭代,我们都 `counter` 变量加 `1`,然后检查 `counter` 是否等于 `10`。当等于 `10` 时,我们使用带有 `counter * 2` 值的 `break` 关键字。在这个循环后,我们使用分号结束指派该值到 `result` 的语句。最后,我们打印 `result` 中的值,本例中为 `20`
我们还可在循环内 `return``break` 只会退出当前循环,而 `return` 则始终会推出当前函数。
> **译注**:这个示例还可写作如下。
>
>
> ```rust
> fn main() {
> let mut counter = 0;
>
> let result = loop {
> counter += 1;
>
> if counter == 10 {
> break
> counter * 2
> }
> };
>
> println! ("结果为:{result}");
> }
> ```
>
> 这似乎与 `break` 后的循环体代码不再执行相矛盾。也可理解为 `counter * 2` 表达式为 `break` 表达式的参数?
### 以循环标签消除歧义
如果咱们有着循环内的循环,`break``continue` 就会应用于那个地方最内层循环。咱们可选择在某个循环上指定一个随后可与 `break``continue` 一起使用*循环标签loop label*,来指定这关键字适用于某个带标签的循环,而不是最内层的那个循环。循环标签必须以单引号开头。下面是个带有两个嵌套循环示例:
咱们有着循环内的循环(译注:嵌套循环)时`break``continue` 就会应用于该处的最内层循环。咱们可选择在某个循环上指定一个 *循环标签loop lable*,随后咱们可与 `break``continue` 一起使用指定这两个关键字会应用到某个带标签的循环,而不是最内层循环。循环标签必须以单引号开头。下面是两个嵌套循环下的一个示例:
```rust
@@ -343,14 +367,14 @@ fn main() {
```
其中外层循环有着标签 `'counting_up`同时他将从 `0` 计数到 `2`而其中的内循环不带标签,会从 `10` 计数到 `9`。未指定标签的第一个 `break` 语句,将只退出那个内部循环。那个 `break 'counting_up;` 语句,则会将外层循环退出。此代码会打印
其中外层循环有着标签 `'counting_up`,他将从 `0` 计数到 `2`不带标签的内循环会从 `10` 计数到 `9`。未指定标签的第一个 `break`,将只退出内层循环。那个 `break 'counting_up;` 语句将退出外层循环。这段代码会打印:
```console
$ cargo run
Compiling loops v0.1.0 (C:\tools\msys64\home\Lenny.Peng\rust-lang-zh_CN\projects\loops)
Finished dev [unoptimized + debuginfo] target(s) in 0.77s
Running `target\debug\loops.exe`
Compiling loops v0.1.0 (/home/hector/rust-lang-zh_CN/projects/loops)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.08s
Running `target/debug/loops`
计数 = 0
剩余 = 10
剩余 = 9
@@ -363,14 +387,12 @@ $ cargo run
```
### 使用 `while` 条件循环
### `while` 下的简化条件循环
**Conditional Loops with `while`**
程序经常需要计算循环内的某个条件。当该条件为 `true` 时,循环就会运行。当条件不再为 `true` 时,程序就会调用 `break`,停止该循环。使用 `loop``if``else``break` 的组合,实现类似这样的行为使可行的;如果咱们愿意,现在就可以在程序中尝试一下。不过,这种模式是如此常见,以致 Rust 为其提供了一种内置的语言结构,称为 `while` 循环。在下面清单 3-3 中,我们使用 `while` 将该程序循环三次,每次会倒计时,然后在循环结束后,打印一条信息并退出。
程序将经常需要评估循环内的条件。当条件为 `true` 时,循环运行。当条件不再为 `true` 时,程序调用 `break`,停止循环。使用 `loop``if``else``break` 的组合实现像是这样的行为是可行的;若咱们愿意,咱们现在就可以在程序中尝试这种做法。然而,这种模式如此常见,以至于 Rust 为其提供了一种内置的语言结构,称为 `while` 循环。在下面清单 3-3 中,我们使用 `while` 循环该程序三次,每次都倒数,然后在循环之后打印一条消息并退出。
<a name="listing_3-3"></a>
文件名:`src/main.rs`
```rust
@@ -387,18 +409,15 @@ fn main() {
}
```
*清单 3-3使用 `while` 循环在条件保持为真期间运行代码*
*清单 3-3使用 `while` 循环在条件求值为 `true` 时运行代码*
这种结构消除了咱们在使用 `loop``if``else`、及 `break`,必然会有的大量嵌套,且更为清晰。在某个条件计算`true` 期间,代码就会运行;否则,他会退出那个循环。
这种结构消除了咱们在使用 `loop``if``else`、及 `break`所需的大量嵌套,且更为清晰。在条件求值`true` ,代码运行;否则,他会退出循环。
### 使用 `for` 遍历集合
**Looping Through a Collection with `for`**
咱们可以选择使用 `while` 结构,来遍历某种集合(比如数组)的元素。作为示例,下面清单 3-4 中的循环,会打印出数组 `a` 中的各个元素。
咱们可选择使用 `while` 结构遍历集合中的元素,比如数组。例如,下面清单 3-4 中的循环会打印数组 `a` 中的各个元素。
文件名:`src/main.rs`
@@ -416,17 +435,17 @@ fn main() {
}
```
*清单 3-4使用 `while` 循环遍历某个集合的各个元素*
*清单 3-4使用 `while` 循环遍历集合的各个元素*
此处,代码会对数组中的元素,进行向上计数。他会索引 `0` 处开始然后循环直到他到达数组中的最终索引(即,当 `index < 5` 不再为 `true` 时)。运行这段代码将打印该数组中的每个元素:
此处,代码会对数组中的元素向上计数。他会索引 `0` 处开始然后循环直到他到达数组中的最终索引(即,当 `index < 5` 不再为 `true` 时)。运行这段代码将打印该数组中的每个元素:
```console
$ cargo run
Compiling loops v0.1.0 (C:\tools\msys64\home\Lenny.Peng\rust-lang-zh_CN\projects\loops)
Finished dev [unoptimized + debuginfo] target(s) in 0.80s
Running `target\debug\loops.exe`
Compiling loops v0.1.0 (/home/hector/rust-lang-zh_CN/projects/loops)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.07s
Running `target/debug/loops`
the value is: 10
the value is: 20
the value is: 30
@@ -435,13 +454,14 @@ the value is: 50
```
如预期那样,所有五个数组值都出现在终端中。即使 `index` 在某一时刻达到 `5`,这个循环也会尝试该数组中,获取第六个值前停止执行。
如预期那样,所有五个数组值都出现在终端中。即使 `index` 在某一时刻达到 `5`,这个循环也会尝试获取该数组中第六个值前停止执行。
但是,这种方法容易出错;如果索引值或测试条件不正确,我们可能会导致程序终止运行。例如,如果咱们将 `a` 这个数组的定义改为有四个元素,忘记将那个条件更新为 `while index < 4`这段代码就会终止运行。此外,由于编译器会添加一些在循环的每次迭代时,执行索引是否在数组边界内条件检查的代码,因此该程序还会运行很慢
但是,这种方法容易出错;索引值或测试条件不正确,我们会导致程序终止运行。例如,咱们将 `a` 这个数组的定义改为有四个元素,忘记将条件更新为 `while index < 4` 时,则这段代码终止运行。他还很慢,因为编译器会添加一些运行时代码,在循环的每次迭代时,执行索引是否在数组边界内条件检查。
作为一种更简洁的替代方法,咱们可使用 `for` 循环,并集合中的每个项目执行一些代码。`for` 循环会看起来如下清单 3-5 中的代码。
作为更简洁的替代方法,咱们可使用 `for` 循环,并集合中的每个项目执行一些代码。`for` 循环会看起来如下清单 3-5 中的代码。
<a name="listing_3-5"></a>
文件名:`src/main.rs`
```rust
@@ -454,16 +474,16 @@ fn main() {
}
```
<a name="listing-3-5"></a> *清单 3-5使用 `for` 循环遍历某个集合的各个元素*
*清单 3-5使用 `for` 循环遍历集合的各个元素*
当咱们运行这段代码时,我们将看到与清单 3-4 同样的输出。更重要的是,我们现在提高了代码的安全性,消除了可能由超出数组末尾,或因遍历不足远而遗漏某些项目等可能导致的错误
当咱们运行这段代码时,我们将看到与清单 3-4 同样的输出。更重要的是,我们现在提高了代码的安全性,消除了超出数组末尾,或因遍历不足远而遗漏某些项目等可能导致的 bug 的可能性。自 `for` 循环生成的机器码也会更加高效,因为索引不需要在每次迭代时都与数组长度比较
使用 `for` 循环,如果咱们改了数组中值的个数,就不需要像清单 3-4 中使用的方法那样,记修改任何其他代码。
使用 `for` 循环时,当咱们改了数组中值的个数时,咱们将不需要向使用 [清单 3-4](#listing_3-4) 中用到的方法那样,记修改任何其他代码。
`for` 循环的安全性和简洁性,使其成为 Rust 中最常用的循环结构。即使是在咱们打算运行某些代码定次数的情下,如清单 3-3 中使用 `while` 循环的倒计时示例中那样,大多数 Rustaceans也会使用 `for` 循环。而要完成那个操作的方法,会用到标准库提供的 `Range`,他可以按顺序生成从一个数字开始到另一个数字结束的所有数字。
`for` 循环的安全性和简洁性,使其成为 Rust 中最常用的循环结构。即使是在咱们打算运行某些代码定次数的情下,如清单 3-3 中使用 `while` 循环的倒计时示例中,大多数 Rustaceans 也会使用 `for` 循环。实现这点的方法,是使用由标准库提供的 `Range`,他按顺序生成从一个数字开始到另一个数字结束的所有数字。
下面是使用 `for` 循环,和另一个我们尚未讨论过的反转范围的方法 `rev`的倒计看起来的样子:
下面是使用 `for` 循环我们尚未讨论过的另一个反转范围的方法 `rev` 的倒计数,看起来的样子:
文件名:`src/main.rs`
@@ -478,22 +498,19 @@ fn main() {
}
```
这段代码更漂亮一些,不是吗?
这段代码好一点,不是吗?
## 本章小结
咱们做到了!这是蔚为壮观的一章:咱们学习了变量、标量与复合数据类型、函数、注释、`if` 表达式和循环!要练习这一章中讨论的概念,请尝试构建程序完成以下操作:
咱们做到了!本章内容客观:咱们学习了变量、标量与复合数据类型、函数、注释、`if` 表达式和循环!为练习本章所讨论的概念,请尝试编写程序来完成以下操作:
- 在华氏温度和摄氏温度之间进行转换;
- 转换华氏和摄氏之间的温度;
- 生成第 n 个斐波拉基数;
- 利用圣诞颂歌 “The Twelve Days of Christmas” 中的重复,而打印出歌词;
- 利用圣诞颂歌 “The Twelve Days of Christmas” 中的重复,而打印出其歌词;
当咱们准备好继续前进时,我们将讨论 Rust 中,一个在其他编程语言中 ** 常见的概念:所有权ownership
当咱们准备好继续前进时,我们将讨论 Rust 中,一个在其他编程语言中 ** 常见的概念:所有权。
## 练习答案