プログラミングをしていると、「同じ処理を何度も繰り返したい!」という場面が出てきますよね。Rustにももちろん、繰り返し処理のための仕組みが用意されています。
その中でも、今回注目するのがRustのloopキーワードです!
他のプログラミング言語に触れたことがある方なら、「whileループ」や「forループ」はお馴染みかもしれません。Rustにも`while`や`for`は存在しますが、`loop`は少し独特な立ち位置にいます。
`loop`は、簡単に言うと「条件なしで永遠に処理を繰り返す」ためのループを作ります。「え、永遠に?止まらないの?」と不安になるかもしれませんが、ご心配なく!ちゃんとループを止める方法も用意されています。
`loop`の主な役割は以下の2つです。
- 明示的に「無限ループ」を作りたい場合に使用する。
- ループの中から`break`を使って特定の値を返すことができる(これは`loop`の面白い特徴!)。
まずは「こういうものがあるんだな」という感じで、気楽に読み進めてみてくださいね。
Rustのloopの基本的な書き方
`loop`の基本的な形はとてもシンプルです。以下のように書きます。
loop { // ここに繰り返したい処理を書く }
`loop`というキーワードの後に、波括弧`{}`で囲まれたブロックが続きます。この波括弧の中に書かれた処理が、無限に繰り返されることになります。
試しに、簡単な例を見てみましょう。次のコードは、コンソールに「無限ループ中!」というメッセージを延々と表示し続けます。
fn main() { loop { println!("無限ループ中!"); } }
このコードを実行すると、ターミナルが「無限ループ中!」で埋め尽くされるはずです。(止めるには `Ctrl + C` を押してくださいね!)
このように、`loop`単体では止まる条件を持たない、というのが基本の動きになります。「じゃあ、どうやって止めるの?」という疑問が湧いてきますよね。次のセクションで、その方法を見ていきましょう!
Rustのloopの使い方
`loop`の基本がわかったところで、ここからはより実践的な使い方をサンプルコードと一緒に学んでいきましょう。無限ループを制御する`break`の使い方や、`loop`から値を返すテクニックなどを紹介します。
`break`でRustのloopを終了する
`loop`で作った無限ループから抜け出すには、`break`キーワードを使います。`break`は、ループ処理をその場で中断し、ループブロックの外に処理を移す命令です。
通常、`if`文と組み合わせて、「特定の条件が満たされたらループを終了する」という使い方をします。例を見てみましょう。カウンター変数が5になったらループを抜けるコードです。
fn main() { let mut counter = 0; // カウンター変数(変更するのでmutが必要) loop { println!("カウンター: {}", counter); counter += 1; // カウンターを1増やす if counter == 5 { println!("カウンターが5になったのでループを抜けます!"); break; // ループを終了! } } println!("ループの外に出ました。"); }
実行結果
カウンター: 0 カウンター: 1 カウンター: 2 カウンター: 3 カウンター: 4 カウンターが5になったのでループを抜けます! ループの外に出ました。
この例では、`loop`ブロックの中で毎回`counter`変数を1ずつ増やしています。
そして、`if counter == 5`の条件が真(true)になったときに`break`が実行され、ループが終了します。`break`がループを抜け出すための合言葉だと覚えておくと良いでしょう。
`break`でRustのloopから値を返す
Rustの`loop`には、他の多くの言語のループにはない面白い機能があります。それは、`loop`自体が「式」であり、`break`を使ってループから値を返すことができる点です!
「ループが値を返す?」と不思議に思うかもしれませんが、これは特定の条件を満たすまで何かを試行し、その結果を使いたい場合に役立ちます。
値を返すには、`break`の後に返したい値を続けます(例: `break 値;`)。そして、`loop`全体を`let`文などで変数に代入することで、返された値を受け取れます。
例として、カウンターが10になったら、そのカウンターの値の2倍をループの結果として返してみましょう。
fn main() { let mut counter = 0; let result = loop { // loop式の結果を変数resultで受け取る counter += 1; println!("試行中... カウンター: {}", counter); if counter == 10 { println!("カウンターが10になったので、結果を返します。"); break counter * 2; // カウンターの2倍の値を返す! } }; // loop式の終わりにはセミコロンが必要な場合が多い println!("ループが返した値: {}", result); // => 20 }
実行結果:
試行中... カウンター: 1 試行中... カウンター: 2 試行中... カウンター: 3 試行中... カウンター: 4 試行中... カウンター: 5 試行中... カウンター: 6 試行中... カウンター: 7 試行中... カウンター: 8 試行中... カウンター: 9 試行中... カウンター: 10 カウンターが10になったので、結果を返します。 ループが返した値: 20
`if counter == 10`の条件が満たされたとき、`break counter * 2;`が実行されます。
`counter`は10なので、`10 * 2`、つまり`20`がループの結果として`result`変数に代入されました。このように、`break`に続けて値を書くことで、ループの「成果物」を持ち出すことができるのです。
ラベル付き`break`でネストしたloopを抜ける
ループ処理は、入れ子(ネスト)になることもあります。つまり、`loop`の中にさらに`loop`があるような構造です。
loop { // 外側のループ // ... loop { // 内側のループ // ... break; // ← これは内側のループしか抜けない! } // ... }
内側のループで`break`を使っても、通常はその内側のループしか抜けられません。外側のループも一気に抜けたい場合はどうすればよいでしょうか?
そこで登場するのが「ラベル付き`break`」です。ループの前に `'ラベル名:` という形式でラベルを付け、`break 'ラベル名;` のように書くことで、指定したラベルが付いているループを直接抜けることができます。
例を見てみましょう。
fn main() { let mut count = 0; println!("外側のループを開始"); 'outer: loop { // 外側のループに 'outer というラベルを付ける println!("外側ループ処理: count = {}", count); let mut remaining = 10; println!(" 内側のループを開始"); loop { // 内側のループ (ラベルなし) println!(" 内側ループ処理: remaining = {}", remaining); if remaining == 9 { println!(" remainingが9なので内側ループをbreak"); break; // 内側のループだけ抜ける } if count == 2 { println!(" countが2なので'outerラベルのループをbreak!"); break 'outer; // 'outerラベルが付いた外側のループを抜ける! } remaining -= 1; } // 内側ループの終わり count += 1; println!(" 内側のループ終了、外側ループの末尾"); } // 'outer ループの終わり println!("外側のループが終了しました。最終count: {}", count); }
実行結果:
外側のループを開始 外側ループ処理: count = 0 内側のループを開始 内側ループ処理: remaining = 10 内側ループ処理: remaining = 9 remainingが9なので内側ループをbreak 内側のループ終了、外側ループの末尾 外側ループ処理: count = 1 内側のループを開始 内側ループ処理: remaining = 10 内側ループ処理: remaining = 9 remainingが9なので内側ループをbreak 内側のループ終了、外側ループの末尾 外側ループ処理: count = 2 内側のループを開始 内側ループ処理: remaining = 10 countが2なので'outerラベルのループをbreak! 外側のループが終了しました。最終count: 2
`count`が2になったとき、内側のループで`break 'outer;`が実行され、外側のループごと処理が終了しているのがわかりますね。
ラベルを使うことで、複雑なループ構造でも意図した場所まで一気に脱出できるようになります。ラベル名はシングルクォート(`'`)で始めるのがルールです。
Rustのloopと他のループ(`while`, `for`)との違い
Rustには`loop`の他に、`while`ループと`for`ループがあります。それぞれの得意なこと、使い分けを簡単に整理しておきましょう。
- `loop`
- 用途: 条件なしの無限ループが基本。`break`で脱出や値の返却を制御する。
- 得意なこと: 終了条件がループ内部のロジックで決まる場合、ループから値を返したい場合、リトライ処理など。
- `while`
- 用途: 指定した条件が真(true)の間、ループを繰り返す。
- 得意なこと: ループの開始前に継続条件が決まっている場合。
- 例: `while 条件式 { ... }`
- `for`
- 用途: 配列や範囲(Range)など、イテレータ(要素の集まりを順番にたどれるもの)の各要素に対して処理を繰り返す。
- 得意なこと: コレクションの全要素を処理する場合。最も安全で推奨されることが多いループ。
- 例: `for item in collection { ... }`
どのループを使うかは状況によりますが、「とりあえず無限ループを作って、後から条件で抜けたい」「ループの結果を値として得たい」という場面では、`loop`が活躍します。
Rustのloopを使う上での注意点
`loop`は便利ですが、いくつか気をつけておきたい点があります。
- `break`の書き忘れに注意!
`loop`は基本的に無限ループなので、`break`を書き忘れたり、`break`に到達する条件がいつまでも満たされなかったりすると、プログラムが意図せず停止しなくなる可能性があります。ループを抜ける条件は必ず設定しましょう。 - ループ条件の変数を更新し忘れない
`if`文などでループの終了条件を判定する場合、その条件で使われている変数がループ内でちゃんと変化するようにしないと、永遠にループを抜けられません。(例: `counter`を増やし忘れるなど) - ネストしたループでの`break`の挙動
前述の通り、ネストした`loop`で単に`break`を使うと、一番内側のループしか抜けません。意図したループを抜けるために、必要に応じてラベル付き`break`を使いましょう。
これらの点に気をつければ、`loop`を安全かつ効果的に活用できますよ!
【まとめ】Rustのloopを理解して活用しよう
今回は、Rustの`loop`キーワードについて、基本的な使い方から応用までを解説しました!
- `loop`は条件なしの無限ループを作成する。
- `break`キーワードでループを終了できる。
- `break 値;`の形で、ループから値を返すことができる(`loop`は式である!)。
- ネストしたループから抜けるにはラベル付き`break`を使う。
- `while`や`for`とは得意な場面が異なる。
- `break`忘れによる無限ループには注意が必要。
`loop`は、一見すると原始的に見えるかもしれませんが、`break`と組み合わせることで柔軟な制御が可能になり、値を返せるという特徴も持っています。
Rustの基本的な制御フローの一つとして、ぜひ使い方をマスターしてください。
0 件のコメント:
コメントを投稿
注: コメントを投稿できるのは、このブログのメンバーだけです。