跳转到内容

Rust while 循环

while 循环会在条件保持为 true 时一直运行。当你清楚自己在等什么,却不确定要等多少次时,它就是合适的工具。

fn main() {
let mut count = 1;
while count <= 5 {
println!("{count}");
count += 1;
}
}

输出:

1
2
3
4
5
▶ 动手试一试

每一轮循环开始之前,都会检查一次条件(count <= 5)。条件一旦变成 false,循环就结束——在这里,就是 count 变成 6 的时候。

同样的事也可以用 loop 加上手写的 if/break 来做,但 while 更直接地表达了你的意思:“只要这个条件成立,就继续。”如果停止条件不适合放在循环开头,再用 loop。

// 用 while——清楚直接
let mut n = 0;
while n < 3 {
println!("{n}");
n += 1;
}
// 用 loop 做同样的事——能用,但要读的代码更多
let mut n = 0;
loop {
if n >= 3 {
break;
}
println!("{n}");
n += 1;
}

while 常用来等一个值达到目标,比如:

fn main() {
let mut balance = 100;
while balance < 200 {
balance += 25;
println!("余额现在是 {balance}");
}
println!("达到目标了!");
}

输出:

余额现在是 125
余额现在是 150
余额现在是 175
余额现在是 200
达到目标了!
▶ 动手试一试

条件可以是任何返回 bool 的表达式,所以你可以用运算符那一页讲过的 && 和 || 组合多个检查,比如 while balance < 200 && months < 12。

有些语言有 do { ... } while (condition) 循环。它先执行循环体,之后才检查条件,所以循环体至少会执行一次。Rust 没有这种循环。取而代之的做法是:用 loop,并把检查放在末尾。

来看一个这点很重要的例子。要数一个数有几位,可以不断除以 10,直到什么都不剩。但数字 0 也有一位,而普通的 while 循环遇到它一次都不会执行:

fn main() {
let number = 0;
// while 循环先检查条件,所以遇到 0 时一次都不执行。
let mut n = number;
let mut digits = 0;
while n > 0 {
digits += 1;
n /= 10;
}
println!("while 认为 {number} 有 {digits} 位");
// loop 加上末尾的检查,循环体至少执行一次。
let mut n = number;
let mut digits = 0;
loop {
digits += 1;
n /= 10;
if n == 0 {
break;
}
}
println!("loop 认为 {number} 有 {digits} 位");
}

输出:

while 认为 0 有 0 位
loop 认为 0 有 1 位
▶ 动手试一试

把 number 改成 4096,两种写法的结果就一样了:4 位。只有当条件从一开始就是假的时候,差别才会显现出来。所以经验法则是:如果循环体必须至少执行一次,就写 loop,并把 if ... { break; } 放在最后。

还有一种 while 循环叫 while let,只要一个值还能匹配某个模式,它就会一直循环。它在 if let 和 while let 那一页有介绍。

计算 1 + 2 + 3 + ……,总和一旦超过 100 就停下。打印一共加了多少个数,以及总和。提示:需要两个 mut 变量,一个存下一个要加的数,一个存总和。

查看答案
fn main() {
let mut next = 1;
let mut total = 0;
while total <= 100 {
total += next;
next += 1;
}
println!("加了 {} 个数,总和是 {total}", next - 1);
}

输出:

加了 14 个数,总和是 105
▶ 动手试一试

最后一轮结束后,next 已经往前多走了一步,所以要打印 next - 1。像这种差一(off-by-one)的细节,值得在脑子里用一个小例子验证一下。

从 1000 开始。只要这个数大于 1,就把它除以 2(整数除法)并打印出来。最后打印一共减半了多少次。提示:n /= 2 会把 n 除以 2,并丢掉余数。

查看答案
fn main() {
let mut n = 1000;
let mut steps = 0;
while n > 1 {
n /= 2;
steps += 1;
println!("{n}");
}
println!("减半 {steps} 次后到达 1");
}

输出:

500
250
125
62
31
15
7
3
1
减半 9 次后到达 1
▶ 动手试一试

125 / 2 的结果是 62,而不是 62.5,因为两个数都是整数。正因为这样,循环才会正好在 1 结束。

  • while condition { ... } 只要条件为 true 就一直重复,每轮执行前都会检查条件。
  • 确保循环里有东西最终会让条件变成 false,否则它会永远运行下去。
  • 只要停止条件能自然地写成“当 X 成立时”,就优先用 while 而不是 loop。
  • Rust 没有 do-while。循环体必须至少执行一次时,用 loop,并把 break 的检查放在末尾。

小测验

1. 导致 while 无限循环最常见的错误是什么?

2. 根据这一页的内容,什么时候应该优先用 while 而不是 loop?

3. 怎样写一个循环体至少执行一次的循环?

得分: 0 / 3