|
|
@ -2,9 +2,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
> [ch03-05-control-flow.md](https://github.com/rust-lang/book/blob/master/src/ch03-05-control-flow.md)
|
|
|
|
> [ch03-05-control-flow.md](https://github.com/rust-lang/book/blob/master/src/ch03-05-control-flow.md)
|
|
|
|
> <br>
|
|
|
|
> <br>
|
|
|
|
> commit 04aa3a45eb72855b34213703718f50a12a3eeec8
|
|
|
|
> commit 2e269ff82193fd65df8a87c06561d74b51ac02f7
|
|
|
|
|
|
|
|
|
|
|
|
通过条件是不是真来决定是否某些代码,或者根据条件是否为真来重复运行一段代码是大部分编程语言的基本组成部分。Rust 代码中最常见的用来控制执行流的结构是`if`表达式和循环。
|
|
|
|
通过条件是不是为真来决定是否执行某些代码,或者根据条件是否为真来重复运行一段代码是大部分编程语言的基本组成部分。Rust 代码中最常见的用来控制执行流的结构是 `if` 表达式和循环。
|
|
|
|
|
|
|
|
|
|
|
|
### `if` 表达式
|
|
|
|
### `if` 表达式
|
|
|
|
|
|
|
|
|
|
|
@ -12,7 +12,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
在 *projects* 目录创建一个叫做 *branches* 的新项目来学习 `if` 表达式。在 *src/main.rs* 文件中,输入如下内容:
|
|
|
|
在 *projects* 目录创建一个叫做 *branches* 的新项目来学习 `if` 表达式。在 *src/main.rs* 文件中,输入如下内容:
|
|
|
|
|
|
|
|
|
|
|
|
<span class="filename">Filename: src/main.rs</span>
|
|
|
|
<span class="filename">文件名: src/main.rs</span>
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
|
fn main() {
|
|
|
|
fn main() {
|
|
|
@ -32,7 +32,7 @@ fn main() {
|
|
|
|
|
|
|
|
|
|
|
|
尝试运行代码,应该能看到如下输出:
|
|
|
|
尝试运行代码,应该能看到如下输出:
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
```text
|
|
|
|
$ cargo run
|
|
|
|
$ cargo run
|
|
|
|
Compiling branches v0.1.0 (file:///projects/branches)
|
|
|
|
Compiling branches v0.1.0 (file:///projects/branches)
|
|
|
|
Running `target/debug/branches`
|
|
|
|
Running `target/debug/branches`
|
|
|
@ -47,7 +47,7 @@ let number = 7;
|
|
|
|
|
|
|
|
|
|
|
|
再次运行程序并查看输出:
|
|
|
|
再次运行程序并查看输出:
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
```text
|
|
|
|
$ cargo run
|
|
|
|
$ cargo run
|
|
|
|
Compiling branches v0.1.0 (file:///projects/branches)
|
|
|
|
Compiling branches v0.1.0 (file:///projects/branches)
|
|
|
|
Running `target/debug/branches`
|
|
|
|
Running `target/debug/branches`
|
|
|
@ -56,7 +56,7 @@ condition was false
|
|
|
|
|
|
|
|
|
|
|
|
另外值得注意的是代码中的条件 **必须** 是 `bool`。如果像看看条件不是 `bool` 值时会发生什么,尝试运行如下代码:
|
|
|
|
另外值得注意的是代码中的条件 **必须** 是 `bool`。如果像看看条件不是 `bool` 值时会发生什么,尝试运行如下代码:
|
|
|
|
|
|
|
|
|
|
|
|
<span class="filename">Filename: src/main.rs</span>
|
|
|
|
<span class="filename">文件名: src/main.rs</span>
|
|
|
|
|
|
|
|
|
|
|
|
```rust,ignore
|
|
|
|
```rust,ignore
|
|
|
|
fn main() {
|
|
|
|
fn main() {
|
|
|
@ -70,7 +70,7 @@ fn main() {
|
|
|
|
|
|
|
|
|
|
|
|
这里 `if` 条件的值是 `3`,Rust 抛出了一个错误:
|
|
|
|
这里 `if` 条件的值是 `3`,Rust 抛出了一个错误:
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
```text
|
|
|
|
error[E0308]: mismatched types
|
|
|
|
error[E0308]: mismatched types
|
|
|
|
--> src/main.rs:4:8
|
|
|
|
--> src/main.rs:4:8
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -83,7 +83,7 @@ error[E0308]: mismatched types
|
|
|
|
|
|
|
|
|
|
|
|
这个错误表明 Rust 期望一个 `bool` 不过却得到了一个整型。Rust 并不会尝试自动地将非布尔值转换为布尔值,不像例如 Ruby 和 JavaScript 这样的语言。必须总是显式地使用 `boolean` 作为 `if` 的条件。例如如果想 要`if` 代码块只在一个数字不等于 `0` 时执行,可以把 `if` 表达式修改为如下:
|
|
|
|
这个错误表明 Rust 期望一个 `bool` 不过却得到了一个整型。Rust 并不会尝试自动地将非布尔值转换为布尔值,不像例如 Ruby 和 JavaScript 这样的语言。必须总是显式地使用 `boolean` 作为 `if` 的条件。例如如果想 要`if` 代码块只在一个数字不等于 `0` 时执行,可以把 `if` 表达式修改为如下:
|
|
|
|
|
|
|
|
|
|
|
|
<span class="filename">Filename: src/main.rs</span>
|
|
|
|
<span class="filename">文件名: src/main.rs</span>
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
|
fn main() {
|
|
|
|
fn main() {
|
|
|
@ -101,7 +101,7 @@ fn main() {
|
|
|
|
|
|
|
|
|
|
|
|
可以将 `else if` 表达式与 `if` 和 `else` 组合来实现多重条件。例如:
|
|
|
|
可以将 `else if` 表达式与 `if` 和 `else` 组合来实现多重条件。例如:
|
|
|
|
|
|
|
|
|
|
|
|
<span class="filename">Filename: src/main.rs</span>
|
|
|
|
<span class="filename">文件名: src/main.rs</span>
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
|
fn main() {
|
|
|
|
fn main() {
|
|
|
@ -121,7 +121,7 @@ fn main() {
|
|
|
|
|
|
|
|
|
|
|
|
这个程序有四个可能的执行路径。运行后应该能看到如下输出:
|
|
|
|
这个程序有四个可能的执行路径。运行后应该能看到如下输出:
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
```text
|
|
|
|
$ cargo run
|
|
|
|
$ cargo run
|
|
|
|
Compiling branches v0.1.0 (file:///projects/branches)
|
|
|
|
Compiling branches v0.1.0 (file:///projects/branches)
|
|
|
|
Running `target/debug/branches`
|
|
|
|
Running `target/debug/branches`
|
|
|
@ -136,7 +136,7 @@ number is divisible by 3
|
|
|
|
|
|
|
|
|
|
|
|
因为 `if` 是一个表达式,我们可以在 `let` 语句的右侧使用它,例如在列表 3-4 中:
|
|
|
|
因为 `if` 是一个表达式,我们可以在 `let` 语句的右侧使用它,例如在列表 3-4 中:
|
|
|
|
|
|
|
|
|
|
|
|
<span class="filename">Filename: src/main.rs</span>
|
|
|
|
<span class="filename">文件名: src/main.rs</span>
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
|
fn main() {
|
|
|
|
fn main() {
|
|
|
@ -151,12 +151,11 @@ fn main() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
<span class="caption">Listing 3-4: Assigning the result of an `if` expression
|
|
|
|
<span class="caption">列表 3-4:将 `if` 的返回值赋值给一个变量</span>
|
|
|
|
to a variable</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
`number` 变量将会绑定到基于 `if` 表达式结果的值。运行这段代码看看会出现什么:
|
|
|
|
`number` 变量将会绑定到基于 `if` 表达式结果的值。运行这段代码看看会出现什么:
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
```text
|
|
|
|
$ cargo run
|
|
|
|
$ cargo run
|
|
|
|
Compiling branches v0.1.0 (file:///projects/branches)
|
|
|
|
Compiling branches v0.1.0 (file:///projects/branches)
|
|
|
|
Running `target/debug/branches`
|
|
|
|
Running `target/debug/branches`
|
|
|
@ -165,7 +164,7 @@ The value of number is: 5
|
|
|
|
|
|
|
|
|
|
|
|
还记得代码块的值是其最后一个表达式的值,以及数字本身也是一个表达式吗。在这个例子中,整个 `if` 表达式的值依赖哪个代码块被执行。这意味着 `if` 的每个分支的可能的返回值都必须是相同类型;在列表 3-4 中,`if` 分支和 `else` 分支的结果都是 `i32` 整型。不过如果像下面的例子那样这些类型并不匹配会怎么样呢?
|
|
|
|
还记得代码块的值是其最后一个表达式的值,以及数字本身也是一个表达式吗。在这个例子中,整个 `if` 表达式的值依赖哪个代码块被执行。这意味着 `if` 的每个分支的可能的返回值都必须是相同类型;在列表 3-4 中,`if` 分支和 `else` 分支的结果都是 `i32` 整型。不过如果像下面的例子那样这些类型并不匹配会怎么样呢?
|
|
|
|
|
|
|
|
|
|
|
|
<span class="filename">Filename: src/main.rs</span>
|
|
|
|
<span class="filename">文件名: src/main.rs</span>
|
|
|
|
|
|
|
|
|
|
|
|
```rust,ignore
|
|
|
|
```rust,ignore
|
|
|
|
fn main() {
|
|
|
|
fn main() {
|
|
|
@ -183,17 +182,17 @@ fn main() {
|
|
|
|
|
|
|
|
|
|
|
|
当运行这段代码,会得到一个错误。`if` 和 `else` 分支的值类型是不相容的,同时 Rust 也准确地表明了在程序中的何处发现的这个问题:
|
|
|
|
当运行这段代码,会得到一个错误。`if` 和 `else` 分支的值类型是不相容的,同时 Rust 也准确地表明了在程序中的何处发现的这个问题:
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
```text
|
|
|
|
error[E0308]: if and else have incompatible types
|
|
|
|
error[E0308]: if and else have incompatible types
|
|
|
|
--> src/main.rs:4:18
|
|
|
|
--> src/main.rs:4:18
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4 | let number = if condition {
|
|
|
|
4 | let number = if condition {
|
|
|
|
| __________________^ starting here...
|
|
|
|
| __________________^
|
|
|
|
5 | | 5
|
|
|
|
5 | | 5
|
|
|
|
6 | | } else {
|
|
|
|
6 | | } else {
|
|
|
|
7 | | "six"
|
|
|
|
7 | | "six"
|
|
|
|
8 | | };
|
|
|
|
8 | | };
|
|
|
|
| |_____^ ...ending here: expected integral variable, found reference
|
|
|
|
| |_____^ expected integral variable, found reference
|
|
|
|
|
|
|
|
|
|
|
|
|
|
= note: expected type `{integer}`
|
|
|
|
= note: expected type `{integer}`
|
|
|
|
found type `&'static str`
|
|
|
|
found type `&'static str`
|
|
|
@ -203,7 +202,7 @@ error[E0308]: if and else have incompatible types
|
|
|
|
|
|
|
|
|
|
|
|
### 使用循环重复执行
|
|
|
|
### 使用循环重复执行
|
|
|
|
|
|
|
|
|
|
|
|
多次执行一段代码是很常用的。为了这个功能,Rust 提供了多种**循环**(*loops*)。一个循环执行循环体中的代码直到结尾并紧接着从回到开头继续执行。为了实验一下循环,让我们创建一个叫做 *loops* 的新项目。
|
|
|
|
多次执行同一段代码是很常用的。为了这个功能,Rust 提供了多种 **循环**(*loops*)。一个循环执行循环体中的代码直到结尾并紧接着从回到开头继续执行。为了实验一下循环,让我们创建一个叫做 *loops* 的新项目。
|
|
|
|
|
|
|
|
|
|
|
|
Rust 有三种循环类型:`loop`、`while` 和 `for`。让我们每一个都试试。
|
|
|
|
Rust 有三种循环类型:`loop`、`while` 和 `for`。让我们每一个都试试。
|
|
|
|
|
|
|
|
|
|
|
@ -213,7 +212,7 @@ Rust 有三种循环类型:`loop`、`while`和`for`。让我们每一个都试
|
|
|
|
|
|
|
|
|
|
|
|
作为一个例子,将 *loops* 目录中的 *src/main.rs* 文件修改为如下:
|
|
|
|
作为一个例子,将 *loops* 目录中的 *src/main.rs* 文件修改为如下:
|
|
|
|
|
|
|
|
|
|
|
|
<span class="filename">Filename: src/main.rs</span>
|
|
|
|
<span class="filename">文件名: src/main.rs</span>
|
|
|
|
|
|
|
|
|
|
|
|
```rust,ignore
|
|
|
|
```rust,ignore
|
|
|
|
fn main() {
|
|
|
|
fn main() {
|
|
|
@ -223,9 +222,9 @@ fn main() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
当执行这个程序,我们会看到`again!`被连续的打印直到我们手动停止程序.大部分终端都支持一个键盘快捷键,ctrl-C,来终止一个陷入无限循环的程序。尝试一下:
|
|
|
|
当执行这个程序,我们会看到 `again!` 被连续的打印直到我们手动停止程序。大部分终端都支持一个键盘快捷键,<span class="keystroke">ctrl-C</span>,来终止一个陷入无限循环的程序。尝试一下:
|
|
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
```text
|
|
|
|
$ cargo run
|
|
|
|
$ cargo run
|
|
|
|
Compiling loops v0.1.0 (file:///projects/loops)
|
|
|
|
Compiling loops v0.1.0 (file:///projects/loops)
|
|
|
|
Running `target/debug/loops`
|
|
|
|
Running `target/debug/loops`
|
|
|
@ -236,7 +235,7 @@ again!
|
|
|
|
^Cagain!
|
|
|
|
^Cagain!
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
符号`^C`代表你在这按下了 ctrl-C。在`^C`之后你可能看到`again!`也可能看不到,这依赖于在接收到终止信号时代码执行到了循环的何处。
|
|
|
|
符号 `^C` 代表你在这按下了<span class="keystroke">ctrl-C</span>。在 `^C` 之后你可能看到 `again!` 也可能看不到,这依赖于在接收到终止信号时代码执行到了循环的何处。
|
|
|
|
|
|
|
|
|
|
|
|
幸运的是,Rust 提供了另一个更可靠的方式来退出循环。可以使用 `break` 关键字来告诉程序何时停止执行循环。还记得我们在第二章猜猜看游戏的 “猜测正确后退出” 部分使用过它来在用户猜对数字赢得游戏后退出程序吗。
|
|
|
|
幸运的是,Rust 提供了另一个更可靠的方式来退出循环。可以使用 `break` 关键字来告诉程序何时停止执行循环。还记得我们在第二章猜猜看游戏的 “猜测正确后退出” 部分使用过它来在用户猜对数字赢得游戏后退出程序吗。
|
|
|
|
|
|
|
|
|
|
|
@ -246,7 +245,7 @@ again!
|
|
|
|
|
|
|
|
|
|
|
|
然而,这个模式太常见了以至于 Rust 为此提供了一个内建的语言结构,它被称为 `while` 循环。下面的例子使用了 `while`:程序循环三次,每次数字都减一。接着,在循环之后,打印出另一个信息并退出:
|
|
|
|
然而,这个模式太常见了以至于 Rust 为此提供了一个内建的语言结构,它被称为 `while` 循环。下面的例子使用了 `while`:程序循环三次,每次数字都减一。接着,在循环之后,打印出另一个信息并退出:
|
|
|
|
|
|
|
|
|
|
|
|
<span class="filename">Filename: src/main.rs</span>
|
|
|
|
<span class="filename">文件名: src/main.rs</span>
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
|
fn main() {
|
|
|
|
fn main() {
|
|
|
@ -266,9 +265,9 @@ fn main() {
|
|
|
|
|
|
|
|
|
|
|
|
#### 使用 `for` 遍历集合
|
|
|
|
#### 使用 `for` 遍历集合
|
|
|
|
|
|
|
|
|
|
|
|
可以使用`while`结构来遍历一个元素集合,比如数组。例如:
|
|
|
|
可以使用 `while` 结构来遍历一个元素集合,比如数组。如下:
|
|
|
|
|
|
|
|
|
|
|
|
<span class="filename">Filename: src/main.rs</span>
|
|
|
|
<span class="filename">文件名: src/main.rs</span>
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
|
fn main() {
|
|
|
|
fn main() {
|
|
|
@ -283,12 +282,11 @@ fn main() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
<span class="caption">Listing 3-5: Looping through each element of a collection
|
|
|
|
<span class="caption">列表 3-5:使用 `while` 循环遍历集合中的每一个元素</span>
|
|
|
|
using a `while` loop</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
这里代码对数组中的元素进行计数。它从索引 `0` 开始,并接着循环直到遇到数组的最后一个索引(这时,`index < 5` 不再为真)。运行这段代码会打印出数组中的每一个元素:
|
|
|
|
这里代码对数组中的元素进行计数。它从索引 `0` 开始,并接着循环直到遇到数组的最后一个索引(这时,`index < 5` 不再为真)。运行这段代码会打印出数组中的每一个元素:
|
|
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
```text
|
|
|
|
$ cargo run
|
|
|
|
$ cargo run
|
|
|
|
Compiling loops v0.1.0 (file:///projects/loops)
|
|
|
|
Compiling loops v0.1.0 (file:///projects/loops)
|
|
|
|
Running `target/debug/loops`
|
|
|
|
Running `target/debug/loops`
|
|
|
@ -303,9 +301,9 @@ the value is: 50
|
|
|
|
|
|
|
|
|
|
|
|
不过这个过程是容易出错的;如果索引长度不正确会导致程序 panic。这也使程序更慢,因为编译器增加了运行时代码来对每次循环的每个元素进行条件检查。
|
|
|
|
不过这个过程是容易出错的;如果索引长度不正确会导致程序 panic。这也使程序更慢,因为编译器增加了运行时代码来对每次循环的每个元素进行条件检查。
|
|
|
|
|
|
|
|
|
|
|
|
可以使用`for`循环来对一个集合的每个元素执行一些代码,来作为一个更有效率替代。`for`循环看起来像这样:
|
|
|
|
可以使用 `for` 循环来对一个集合的每个元素执行一些代码,来作为一个更有效率的替代。`for` 循环看起来像这样:
|
|
|
|
|
|
|
|
|
|
|
|
<span class="filename">Filename: src/main.rs</span>
|
|
|
|
<span class="filename">文件名: src/main.rs</span>
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
|
fn main() {
|
|
|
|
fn main() {
|
|
|
@ -317,10 +315,9 @@ fn main() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
<span class="caption">Listing 3-6: Looping through each element of a collection
|
|
|
|
<span class="caption">列表 3-6:使用 `for` 循环遍历集合中的每一个元素</span>
|
|
|
|
using a `for` loop</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
当运行这段代码,将看到与列表 3-5 一样的输出。更为重要的是,我们增强了代码安全性并消除了出现可能会导致超出数组的结尾或遍历长度不够而缺少一些元素这类 bug 机会。
|
|
|
|
当运行这段代码,将看到与列表 3-5 一样的输出。更为重要的是,我们增强了代码安全性并消除了出现可能会导致超出数组的结尾或遍历长度不够而缺少一些元素这类 bug 的机会。
|
|
|
|
|
|
|
|
|
|
|
|
例如,在列表 3-5 的代码中,如果从数组 `a` 中移除一个元素但忘记更新条件为 `while index < 4`,代码将会 panic。使用`for`循环的话,就不需要惦记着在更新数组元素数量时修改其他的代码了。
|
|
|
|
例如,在列表 3-5 的代码中,如果从数组 `a` 中移除一个元素但忘记更新条件为 `while index < 4`,代码将会 panic。使用`for`循环的话,就不需要惦记着在更新数组元素数量时修改其他的代码了。
|
|
|
|
|
|
|
|
|
|
|
@ -328,7 +325,7 @@ using a `for` loop</span>
|
|
|
|
|
|
|
|
|
|
|
|
下面是一个使用 `for` 循环来倒计时的例子,它还使用了一个我们还未讲到的方法,`rev`,用来反转 range:
|
|
|
|
下面是一个使用 `for` 循环来倒计时的例子,它还使用了一个我们还未讲到的方法,`rev`,用来反转 range:
|
|
|
|
|
|
|
|
|
|
|
|
<span class="filename">Filename: src/main.rs</span>
|
|
|
|
<span class="filename">文件名: src/main.rs</span>
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
|
fn main() {
|
|
|
|
fn main() {
|
|
|
|