コンテンツにスキップ

Rustのloop

Rustには、コードをくり返す方法が3つあります。loop、while、for です。このページでは、その中でいちばんシンプルでストレートな loop を扱います。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; } がなければ、このプログラムは永遠に数字を表示し続けます。

while や for にはできなくて、loop にはできることがあります。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 x = 5; と同じように、let 文の最後には ; が必要です。

while true ではなく loop を使うのはなぜ?

Section titled “while true ではなく loop を使うのはなぜ?”

ほかの言語から来た人は、無限ループを 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 についてはそう判断しません。条件の値までは見ないからです。この違いは、ループの中で変数に値を入れるときに、はっきり表れます。

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 なら、println! にたどり着く道はその break しかないとRustにはわかるので、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から始めて、2倍にし続けます。値を返す break を使って1000を超える最初の数を求め、何回2倍にしたかも数えてください。ヒント:ループの外に 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}回2倍にして{power}");
}

実行結果:

10回2倍にして1024
▶ 実際に試してみよう

break value; はループを終わらせて、value を power に渡します。カウンタはループの外にあるので、ループが終わった後も値が残っています。

  • loop は break で止めるまでずっとくり返します。抜け出す break を必ず用意しましょう。
  • break 値; はループを止めると同時に、値 をループ式全体の結果として返します。
  • while true ではなく loop と書きましょう。loop の本体は必ず実行されるとRustが知っているので、変数のチェックに役立ちます。
  • 決まった回数を数えるときではなく、「ある条件が起きるまで」くり返したいときに loop を使いましょう。

理解度チェック

1. 何も付けない loop が永遠に回り続けるのを止めるのは何ですか?

2. ループの中で break count * 2; と書くと、どうなりますか?

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

4. Rustで while true { ... } と書くと、どうなりますか?

スコア: 0 / 4