コンテンツにスキップ

Rustのwhile

while ループは、条件が true である間ずっと動き続けます。何回かかるかは正確にわからなくても、何を待っているのかはわかっている。そんなときにぴったりの道具です。

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

実行結果:

1
2
3
4
5
▶ 実際に試してみよう

条件(count <= 5)は、ループを1回まわる前に毎回チェックされます。条件が 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 (条件) というループがあります。本体を先に実行してから条件をチェックするので、本体は必ず1回は実行されます。Rustにはこれがありません。代わりに loop を使い、チェックを最後に置きます。

これが効いてくる例を見てみましょう。数の桁数を数えるには、何も残らなくなるまで10で割り続ければよいですね。でも 0 という数も1桁なのに、ふつうの 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と最後のチェックなら、本体は少なくとも1回実行される
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桁で一致します。違いが出るのは、最初から条件が偽のときだけです。そこで目安としては、本体を少なくとも1回は実行しなければならないなら、loop を使って一番下に if ... { break; } を書きましょう。

while ループにはもう1種類、値がパターンに当てはまる間続ける while let があります。これはif let / while letのページで扱っています。

1 + 2 + 3 + … と足していき、合計が100を超えたところで止めてください。いくつの数を足したかと、合計を表示しましょう。ヒント:mut の変数が2つ必要です。1つは次に足す数、もう1つは合計です。

解答を見る
fn main() {
let mut next = 1;
let mut total = 0;
while total <= 100 {
total += next;
next += 1;
}
println!("{}個の数を足して、合計は{total}", next - 1);
}

実行結果:

14個の数を足して、合計は105
▶ 実際に試してみよう

最後の1回が終わった時点で、next はもう1つ先に進んでいます。だから next - 1 を表示しているのです。こうした「1つずれ」の細かい点は、小さな例を頭の中でたどって確かめる価値があります。

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!("1になるまで{steps}回半分にしました");
}

実行結果:

500
250
125
62
31
15
7
3
1
1になるまで9回半分にしました
▶ 実際に試してみよう

125 / 2 は 62.5 ではなく 62 になります。どちらの数も整数だからです。そのおかげで、ループはちょうど1で終わります。

  • while 条件 { ... } は、条件が true である間くり返します。条件は毎回、まわる前にチェックされます。
  • ループの中のどこかで、いずれ条件が false になるようにしましょう。そうしないと永遠に動き続けます。
  • 止める条件が「Xが成り立つ間」という形で自然に書けるときは、loop より while を選びましょう。
  • Rustにはdo-whileがありません。本体を少なくとも1回実行したいときは、loop を使い、break のチェックを最後に置きます。

理解度チェック

1. while ループが無限ループになってしまう典型的な間違いはどれですか?

2. このページによると、loop より while を選ぶべきなのはどんなときですか?

3. 本体が必ず少なくとも1回実行されるループは、どう書きますか?

スコア: 0 / 3