控制流
能够根据条件是否为 和 to 来运行某些代码
当 Condition 是基本构建块时,重复运行一些代码
在大多数编程语言中。可让您控制的最常见结构
Rust 代码的执行流程是表达式和循环。true
true
if
如果
表达 式
表达式允许您根据条件对代码进行分支。你
提供一个条件,然后声明“如果满足此条件,则运行此块
的代码。如果不满足条件,请不要运行此代码块。if
在 projects 目录中创建一个名为 branches 的新项目以进行浏览
表达式。在 src/main.rs 文件中,输入以下内容:if
文件名: src/main.rs
fn main() { let number = 3; if number < 5 { println!("condition was true"); } else { println!("condition was false"); } }
所有表达式都以 keyword 开头,后跟 condition。在
在这种情况下,条件检查变量是否具有
值小于 5。我们将要执行的代码块(如果条件紧跟在条件之后)放在大括号内。代码块
与表达式中的条件相关联有时称为 arms,
就像我们在“比较
猜秘密数字“部分。if
if
number
true
if
match
或者,我们还可以包含一个表达式,我们选择这样做
在这里,为了给程序提供一个替代的代码块来执行,如果
condition 评估为 。如果您未提供表达式,并且
条件是 ,程序将跳过该块并继续前进
到下一段代码。else
false
else
false
if
尝试运行此代码;您应该会看到以下输出:
$ cargo run
Compiling branches v0.1.0 (file:///projects/branches)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.31s
Running `target/debug/branches`
condition was true
让我们尝试将 value of 更改为构成条件的值,看看会发生什么:number
false
fn main() {
let number = 7;
if number < 5 {
println!("condition was true");
} else {
println!("condition was false");
}
}
再次运行该程序,并查看输出:
$ cargo run
Compiling branches v0.1.0 (file:///projects/branches)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.31s
Running `target/debug/branches`
condition was false
还值得注意的是,此代码中的 condition 必须是 .如果
条件不是 ,我们将收到错误。例如,尝试运行
以下代码:bool
bool
文件名: src/main.rs
fn main() {
let number = 3;
if number {
println!("number was three");
}
}
该条件的计算结果为这个时间的值,并且 Rust 会抛出一个
错误:if
3
$ cargo run
Compiling branches v0.1.0 (file:///projects/branches)
error[E0308]: mismatched types
--> src/main.rs:4:8
|
4 | if number {
| ^^^^^^ expected `bool`, found integer
For more information about this error, try `rustc --explain E0308`.
error: could not compile `branches` (bin "branches") due to 1 previous error
该错误表明 Rust 期望 a 但得到的是整数。与
Ruby 和 JavaScript 等语言,Rust 不会自动尝试
将非布尔类型转换为布尔类型。您必须明确,并始终提供 Boolean 作为其条件。如果我们希望代码块运行
例如,只有当一个数字不等于 时,我们才能将表达式更改为以下内容:bool
if
if
0
if
文件名: src/main.rs
fn main() { let number = 3; if number != 0 { println!("number was something other than zero"); } }
运行此代码将打印 .number was something other than zero
使用 else if
处理多个条件
您可以通过在表达式中组合 and 来使用多个条件。例如:if
else
else if
文件名: src/main.rs
fn main() { let number = 6; if number % 4 == 0 { println!("number is divisible by 4"); } else if number % 3 == 0 { println!("number is divisible by 3"); } else if number % 2 == 0 { println!("number is divisible by 2"); } else { println!("number is not divisible by 4, 3, or 2"); } }
该程序有四种可能的路径。运行后,您应该 请参阅以下输出:
$ cargo run
Compiling branches v0.1.0 (file:///projects/branches)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.31s
Running `target/debug/branches`
number is divisible by 3
当此程序执行时,它会依次检查每个表达式并执行
条件计算结果为 的第一个主体。请注意,即使
虽然 6 可以被 2 整除,但我们看不到输出 ,
我们也看不到区块中的文本。这是因为 Rust 只执行第一个条件的块,一旦找到一个,它甚至不会检查其余的。if
true
number is divisible by 2
number is not divisible by 4, 3, or 2
else
true
使用过多的表达式会使代码变得混乱,因此,如果表达式的表达式过多
而不是 1,则可能需要重构代码。第 6 章描述了一个强大的
Rust 分支结构适用于这些情况。else if
match
在 let
语句中使用 if
因为 is 是一个表达式,我们可以在语句的右侧使用它来将结果分配给一个变量,如示例 3-2 所示。if
let
该变量将根据表达式的结果绑定到一个值。运行以下代码以查看会发生什么:number
if
$ cargo run
Compiling branches v0.1.0 (file:///projects/branches)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.30s
Running `target/debug/branches`
The value of number is: 5
请记住,代码块的计算结果为其中的最后一个表达式,并且
数字本身也是表达式。在这种情况下,
整个表达式取决于执行的代码块。这意味着
有可能成为 必须
相同类型;在示例 3-2 中,arm 和 arm 的结果都是整数。如果类型不匹配,如下所示
example,我们将得到一个错误:if
if
if
else
i32
文件名: src/main.rs
fn main() {
let condition = true;
let number = if condition { 5 } else { "six" };
println!("The value of number is: {number}");
}
当我们尝试编译此代码时,我们将收到一个错误。和 arms
具有不兼容的值类型,并且 Rust 会准确指示
在程序中找到问题:if
else
$ cargo run
Compiling branches v0.1.0 (file:///projects/branches)
error[E0308]: `if` and `else` have incompatible types
--> src/main.rs:4:44
|
4 | let number = if condition { 5 } else { "six" };
| - ^^^^^ expected integer, found `&str`
| |
| expected because of this
For more information about this error, try `rustc --explain E0308`.
error: could not compile `branches` (bin "branches") due to 1 previous error
块中的表达式计算结果为整数,而
该块的计算结果为字符串。这不起作用,因为变量必须
只有一个类型,并且 Rust 需要在编译时明确地知道变量是什么类型。知道 的类型可以让
编译器验证类型在我们使用 .Rust 不会
如果 的类型仅在运行时确定,则能够执行此作;这
编译器会更复杂,并且对代码的保证更少
如果它必须跟踪任何变量的多个假设类型。if
else
number
number
number
number
使用 Loops 重复
多次执行一个代码块通常很有用。对于此任务, Rust 提供了几个循环,这些循环将遍历循环内的代码 body 到最后,然后立即从头开始。试验 使用 Loops,让我们创建一个名为 Loops 的新项目。
Rust 有三种类型的循环:、 和 。让我们逐一尝试。loop
while
for
使用循环
重复代码
该关键字告诉 Rust 一遍又一遍地执行一段代码
永远或直到您明确告诉它停止。loop
例如,将 loops 目录中的 src/main.rs 文件更改为 喜欢这个:
文件名: src/main.rs
fn main() {
loop {
println!("again!");
}
}
当我们运行这个程序时,我们将看到一遍又一遍的打印
直到我们手动停止程序。大多数终端都支持键盘快捷键 - 中断卡在连续
圈。试一试:again!
ctrlc
$ cargo run
Compiling loops v0.1.0 (file:///projects/loops)
Finished dev [unoptimized + debuginfo] target(s) in 0.29s
Running `target/debug/loops`
again!
again!
again!
again!
^Cagain!
该符号表示您按 - 的位置。你
可能会也可能不会看到 后面打印的单词,具体取决于位置
代码在接收到中断信号时处于循环中。^C
ctrlcagain!
^C
幸运的是,Rust 还提供了一种使用代码跳出循环的方法。你
可以在循环中放置关键字以告诉程序何时停止
执行循环。回想一下,我们在第 2 章的“正确猜测后退出”部分的猜谜游戏中这样做了,当用户赢得游戏时退出程序
猜对数字。break
我们还在 guessing game 中使用了 guessing game,它在循环中告诉程序
跳过此循环迭代中的任何剩余代码,并转到
next 迭代。continue
从循环返回值
a 的用途之一是重试您知道可能会失败的作,例如
检查线程是否已完成其作业。您可能还需要将
该作的结果从循环中传出到代码的其余部分。待办事项
这样,您可以在 Zip 的表达式 you 之后添加要返回的值
用于停止循环;该值将从循环中返回,因此您可以
使用它,如下所示:loop
break
fn main() { let mut counter = 0; let result = loop { counter += 1; if counter == 10 { break counter * 2; } }; println!("The result is {result}"); }
在循环之前,我们声明一个名为 的变量并将其初始化为 。然后我们声明一个名为 的变量来保存从
循环。在循环的每次迭代中,我们都会向变量
然后检查 是否等于 。如果是,我们使用值为 .在循环之后,我们使用
分号结束将值分配给 的语句。最后,我们
打印 中的值,在本例中为 。counter
0
result
1
counter
counter
10
break
counter * 2
result
result
20
您也可以从 Loop 内部。While 仅退出当前
loop,始终退出当前函数。return
break
return
Loop 标签以消除多个 Loop 之间的歧义
如果你在循环中有循环,并且应用到最里面的
循环。您可以选择在循环上指定一个循环标签,该标签
然后,您可以使用 with 或 来指定这些关键字
应用于带标签的 Loop,而不是最内层的 Loop。循环标签必须开始
替换为单引号。下面是一个包含两个嵌套循环的示例:break
continue
break
continue
fn main() { let mut count = 0; 'counting_up: loop { println!("count = {count}"); let mut remaining = 10; loop { println!("remaining = {remaining}"); if remaining == 9 { break; } if count == 2 { break 'counting_up; } remaining -= 1; } count += 1; } println!("End count = {count}"); }
外部循环具有标签 ,它将从 0 到 2 计数。
没有标签的内循环从 10 到 9 倒计时。第一个
未指定 Label 将仅退出内部循环。该语句将退出外部循环。此代码打印:'counting_up
break
break 'counting_up;
$ cargo run
Compiling loops v0.1.0 (file:///projects/loops)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.58s
Running `target/debug/loops`
count = 0
remaining = 10
remaining = 9
count = 1
remaining = 10
remaining = 9
count = 2
remaining = 10
End count = 2
带有 while
的条件循环
程序通常需要评估循环中的条件。虽然
condition 为 ,则循环运行。当条件不再为 时,
程序调用 ,停止循环。可以实现行为
像这样使用 、、 和 ;你可以
如果您愿意,现在就在一个程序中尝试一下。然而,这种模式非常普遍
Rust 有一个内置的语言结构,称为循环。在
示例 3-3,我们用来循环程序 3 次,每次倒数
time,然后在循环后打印一条消息并退出。true
true
break
loop
if
else
break
while
while
这个结构消除了大量嵌套,如果你使用 、、 和 ,这将是必要的,而且它更清晰。虽然条件
计算结果为 ,则代码运行;否则,它将退出循环。loop
if
else
break
true
使用 for
循环遍历集合
您还可以使用 construct 遍历
集合,例如数组。例如,示例 3-4 中的循环打印了每个
元素。while
a
在这里,代码通过数组中的元素进行计数。它从 index 开始,然后循环,直到到达数组中的最后一个索引(即
when 不再 )。运行此代码将打印每个
元素中:0
index < 5
true
$ cargo run
Compiling loops v0.1.0 (file:///projects/loops)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.32s
Running `target/debug/loops`
the value is: 10
the value is: 20
the value is: 30
the value is: 40
the value is: 50
正如预期的那样,所有 5 个 array 值都显示在终端中。即使会在某个时候达到值 ,循环也会在尝试之前停止执行
从数组中获取第六个值。index
5
但是,这种方法容易出错;如果出现以下情况,我们可能导致程序 panic
索引值或测试条件不正确。例如,如果您更改了
数组的定义为具有四个元素,但忘记更新
条件设置为 SET ,则代码将 panic。它也很慢,因为
编译器添加运行时代码以执行条件检查是否
index 在循环的每次迭代中都在数组的边界内。a
while index < 4
作为更简洁的替代方案,您可以使用循环并执行一些代码
对于集合中的每个项目。循环类似于示例 3-5 中的代码。for
for
当我们运行这段代码时,我们将看到与示例 3-4 中相同的输出。更多 重要的是,我们现在提高了代码的安全性,并消除了 因超出数组末尾或不超出数组末尾而可能导致的 bug 的几率 走得足够远,缺少一些项目。
使用循环,如果
您更改了数组中值的数量,就像使用 Method
在示例 3-4 中使用。for
Loop 的安全性和简洁性使其成为最常用的 Loop
构造。即使在您想要运行某些代码 a
一定次数,如使用循环的 Countdown 示例
在示例 3-3 中,大多数 Rustacean 都会使用循环。实现目标的方法
将使用标准库提供的 ,该库会生成
所有数字按顺序从一个数字开始,在另一个数字之前结束
数。for
while
for
Range
以下是使用 Loop 和另一种方法的倒计时
我们还没有讨论 ,来反转范围:for
rev
文件名: src/main.rs
fn main() { for number in (1..4).rev() { println!("{number}!"); } println!("LIFTOFF!!!"); }
这段代码好一点,不是吗?
总结
你成功了!这是一个相当大的章节:您了解了变量、标量
以及复合数据类型、函数、注释、表达式和循环!自
练习本章中讨论的概念,尝试构建程序以
执行以下作:if
- 在华氏度和摄氏度之间转换温度。
- 生成第 n个斐波那契数。
- 打印圣诞颂歌“The Twelve Days of Christmas”的歌词。 利用歌曲中的重复。
当你准备好继续时,我们将讨论 Rust 中一个在其他编程语言中通常不存在的概念:所有权。
本文档由官方文档翻译而来,如有差异请以官方英文文档(https://doc.rust-lang.org/)为准