3.6 控制流:循环
3.6.0. 写在正文之前
欢迎来到Rust自学的第三章,一共有6个小节,分别是:
- 变量与可变性
- 数据类型:标量类型
- 数据类型:复合类型
- 函数和注释
- 控制流:
if else - 控制流:循环(本文)
通过第二章的小游戏(没看的初学者强烈建议看一下),相信你已经学会了基本的Rust语法,而在第三章我们将更深一层,了解Rust中的通用的编程概念。
3.6.1. Rust的循环
Rust一共提供了三种循环:
loopwhilefor
3.6.2. loop循环
loop关键字告诉Rust反复执行一段代码,直到被告知停止。以下是一个例子,它会不断打印6657 up up!。
fn main(){
loop {
println!("6657 up up!");
}
}
可以在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,每次循环递增1。- 当
counter等于10时,break退出循环,并返回counter * 2(即20)。 loop是一个表达式,其返回值是传给break的值,因此可以直接赋给result。result最终打印为20。
代码特点:
- Rust的
loop是表达式,因此其结果可以直接绑定到变量。 break可以携带返回值(这里是counter * 2),并将其作为loop的结果。let语句要求赋值表达式后加分号,因此loop的结束大括号}后需要加分号。
3.6.3. while条件循环
while循环在每次执行循环体之前都要判断一次条件。
fn main() {
let mut countdown = 10; // 倒计时从10开始
println!("Rocket Launch Countdown:");
while countdown > 0 {
println!("T-minus {}...", countdown);
countdown -= 1; // 每次减少1
}
println!("🚀 Liftoff!");
println!("Houston, we have a problem.");
}
这是一个简单的while循环示例,其运行结果是:
Rocket Launch Countdown:
T-minus 10...
T-minus 9...
T-minus 8...
T-minus 7...
T-minus 6...
T-minus 5...
T-minus 4...
T-minus 3...
T-minus 2...
T-minus 1...
🚀 Liftoff!
Houston, we have a problem.
3.6.4. 使用for循环遍历集合
当然也可以使用while和loop来遍历集合,但那样既容易出错又低效。
这是一个使用while的例子:
fn main() {
let numbers = [10, 20, 30, 40, 50];
let mut index = 0;
println!("Using while loop:");
while index < 5 {
println!("Number at index {}: {}", index, numbers[index]);
index += 1;
}
}
使用while时极有可能因索引越界而触发panic,而且运行速度较慢,因为每次都要检查index < 5这个条件。
这是一个使用for的例子(实现同样的效果):
fn main() {
let numbers = [10, 20, 30, 40, 50];
println!("Using for loop:");
for (index, number) in numbers.iter().enumerate() {
println!("Number at index {}: {}", index, number);
}
}
1. numbers.iter()
- 调用集合
numbers的.iter()方法,创建一个不可变迭代器,用于逐个访问元素。在Rust中,for循环并不直接操作集合,而是操作实现了Iterator特征的迭代器。.iter()是Vec及其他集合常用的方法,用于生成指向元素引用的迭代器。for循环简洁清晰,可以对集合中的每个元素执行代码。由于其安全性和简洁性,它在Rust中使用最多。
2. .enumerate()
• 为迭代器的每个元素附加一个索引。索引从0开始,是一个usize值。.enumerate()将迭代器的每个元素包装成(index, value)的形式,其中index是元素在集合中的位置,value是当前迭代器指向的元素。.enumerate()返回一个新的迭代器,其元素类型是(usize, &T),其中T是集合中元素的类型。这里,numbers是一个i32数组,因此&T是&i32。
3. for (index, number) in ...
• for循环支持解构元组。(index, number)表示我们直接将enumerate()产生的(usize, &T)元组解构成两个变量:index,当前元素的索引;以及number,当前元素的引用(不可变)。
假设numbers是[10, 20, 30, 40, 50],执行过程如下:
- 调用
numbers.iter()创建迭代器。 - 调用
.enumerate(),生成(索引, 元素引用)的迭代器。 for循环解构出索引和元素:- 第一次循环:
index = 0, number = &10 - 第二次循环:
index = 1, number = &20 - 第三次循环:
index = 2, number = &30 - …
- 第一次循环:
- 打印
index和number,输出每个元素的索引和值。
由于for循环安全且简洁,所以它在Rust中使用最多。
3.6.5. Range
范围(Range)由标准库提供。你可以通过范围生成两个边界之间的数字:a..b不含结束,而a..=b包含结束。使用rev方法可以反转一个范围。
fn main() {
println!("Rocket Launch Countdown:");
for countdown in (1..=10).rev() {
println!("T-minus {}...", countdown);
}
println!("🚀 Liftoff!");
println!("Houston, we have a problem.");
}
这个例子使用for循环、Range和rev,实现了上文while示例中的火箭倒计时。
代码解析
(1..=10):- 这是一个
Range,表示从1到10(包含两端)。 ..=是包含上限的范围操作符。
- 这是一个
.rev():- 反转迭代器,生成一个从10到1的递减序列。