Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

3.6 控制流:循环

3.6.0. 写在正文之前

欢迎来到Rust自学的第三章,一共有6个小节,分别是:

通过第二章的小游戏(没看的初学者强烈建议看一下),相信你已经学会了基本的Rust语法,而在第三章我们将更深一层,了解Rust中的通用的编程概念。

3.6.1. Rust的循环

Rust一共提供了三种循环:

  • loop
  • while
  • for

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循环遍历集合

当然也可以使用whileloop来遍历集合,但那样既容易出错又低效。 这是一个使用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],执行过程如下:

  1. 调用numbers.iter()创建迭代器。
  2. 调用.enumerate(),生成(索引, 元素引用)的迭代器。
  3. for循环解构出索引和元素:
    • 第一次循环:index = 0, number = &10
    • 第二次循环:index = 1, number = &20
    • 第三次循环:index = 2, number = &30
  4. 打印indexnumber,输出每个元素的索引和值。

由于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循环、Rangerev,实现了上文while示例中的火箭倒计时。

代码解析

  1. (1..=10):
    • 这是一个Range,表示从1到10(包含两端)。
    • ..=是包含上限的范围操作符。
  2. .rev():
    • 反转迭代器,生成一个从10到1的递减序列。