跳转到内容

Rust loop 循环

Rust 有三种重复执行代码的方式:loop、while 和 for。这一页讲 loop,它是三者中最简单、最直接的一种——一直重复下去,直到你让它停下来。

fn main() {
let mut count = 0;
loop {
count += 1;
println!("{count}");
if count == 3 {
break;
}
}
}

输出:

1
2
3
▶ 动手试一试

loop 本身永远不会停,让它结束的是 break。要是没有那句 if count == 3 { break; },这个程序就会一直打印下去。

有一件事只有 loop 能做,while 和 for 做不到:你可以在 break 后面写一个值,这个值就成了整个循环的结果:

fn main() {
let mut count = 0;
let result = loop {
count += 1;
if count == 10 {
break count * 2; // 停下来,并交回一个值
}
};
println!("{result}");
}

输出:

20
▶ 动手试一试

像“不断重试直到成功,然后把结果给我”这种场景,用它就很方便——在真实的代码里,你见到这种写法的次数会比想象中多。

注意循环结尾的 } 后面有个分号。整个 loop { ... } 是 let result = ...; 的右边部分,所以这条 let 语句末尾需要 ;,就像 let x = 5; 一样。

如果你学过别的语言,可能会把无限循环写成 while true { ... }。这在 Rust 里也能运行,但编译器会给出警告:

warning: denote infinite loops with `loop { ... }`
--> src/main.rs:5:5
|
5 | while true {
| ^^^^^^^^^^ help: use `loop`

这条警告的意思是:请用 loop { ... } 来表示无限循环。这不只是风格问题。Rust 知道 loop 的循环体一定会执行,而且唯一的出口就是 break。对于 while true,Rust 不会这样假设,因为它不去看条件的值。当一个变量在循环里才被赋值时,这一点就很关键了:

fn main() {
let mut n = 1;
let answer; // 还没有值
loop {
if n * n > 50 {
answer = n * n; // 在离开之前给它赋值
break;
}
n += 1;
}
println!("第一个大于 50 的平方数是 {answer}");
}

输出:

第一个大于 50 的平方数是 64
▶ 动手试一试

用 loop 时,Rust 能看出走到 println! 的唯一途径就是那个 break,所以 answer 一定已经赋值了。把 loop 改成 while true,你就会得到 error[E0381]: used binding answer is possibly-uninitialized(使用的绑定 answer 可能未初始化)。编译器认为,这个循环有可能一次 if 都没执行就结束了。

实际写代码时,这个程序最简洁的写法是用上面学过的带值的 break:let answer = loop { ... break n * n; ... };。不管哪种写法,道理都一样:当你的意思是“一直循环,直到我 break”,就写 loop。

如果你事先不知道要重复多少次——你是在等某个条件成立,而不是数过一个范围或一个列表——那 loop 就是合适的选择。如果是计数或遍历集合,while 和 for(接下来会讲)通常读起来更清楚。

用 loop 打印从 5 到 1 的倒计时,然后打印 发射!。提示:从 let mut n = 5; 开始,等 n 到 0 时 break。

查看答案
fn main() {
let mut n = 5;
loop {
if n == 0 {
break;
}
println!("{n}...");
n -= 1;
}
println!("发射!");
}

输出:

5...
4...
3...
2...
1...
发射!
▶ 动手试一试

检查写在最前面,所以当 n 变成 0 时,循环在打印 0... 之前就停下了。

从 1 开始不断翻倍。用带值的 break 得到第一个大于 1000 的数,并统计一共翻倍了多少次。提示:在循环外面放一个 mut 计数器,写成 let power = loop { ... };。

查看答案
fn main() {
let mut value = 1;
let mut doublings = 0;
let power = loop {
value *= 2;
doublings += 1;
if value > 1000 {
break value;
}
};
println!("翻倍 {doublings} 次后得到 {power}");
}

输出:

翻倍 10 次后得到 1024
▶ 动手试一试

break value; 结束循环,并把 value 交给 power。计数器放在循环外面,所以循环结束后它的值还在。

  • loop 会一直重复,直到遇到 break 才停下——一定要确保有一个 break。
  • break value; 会停止循环,并且把 value 作为整个循环表达式的结果返回。
  • 要写 loop,不要写 while true:Rust 知道 loop 的循环体一定会执行,这能帮它检查你的变量。
  • 当你要“一直重复,直到某件事发生”时用 loop;要数固定的次数时就不必用它。

小测验

1. 是什么让一个单纯的 loop 不会永远运行下去?

2. 在循环里写 break count * 2; 有什么效果?

3. 根据这一页的内容,什么时候 loop 比 while 或 for 更合适?

4. 在 Rust 里写 while true { ... } 会怎样?

得分: 0 / 4