Rustのループと反復処理:`loop`、`while`、`for`の習得
Rustのループは、単なる「無限ループ」というだけでなく、最後の計算結果を「出力」します。
3種類のループにはそれぞれ独自の用途があります:loop 無限ループ(戻り値あり)、while 条件付きループ、for 集合の反復処理です。
1. 学習内容
loopを使用して無限ループを作成し、値を返す- 条件付きループには
whileを使用してください forを使用して、集合や範囲を反復処理するbreakおよびcontinueを使用してループ処理を制御します- ネストされたループから抜け出すには、ループラベルを使用します
- 範囲式
0..nおよび0..=nの使用
2. 迷路探検家の物語
(1) 苦しみ:迷路で道に迷う
アレックスは迷路ゲームを開発しています。この迷路は、3層の入れ子構造になっています:
Outer layer (Dungeon) -> Middle layer (Room) -> Inner layer (Treasure Chest)
プレイヤーは出口を見つけるためにどんどん奥へ進まなければならないが、コードを書くのが難しいと感じている:
- 宝箱の中から鍵を見つけた後、上の階へジャンプしたかったのですが、
breakは1階分しかジャンプできません - ドアを開けるにはすべての部屋を探索しなければならない――
whileとfor、どちらを使うべきだろうか? - 迷路の階数は決まっていますが、各階にある部屋の数は異なります。
「もし各ループにラベルを付けられれば、好きな場所にジャンプできるのに……」
(2) Rustにおけるループの解決策
fn main() {
let mut found_exit = false;
let maze = [
["empty", "empty", "key"],
["empty", "monster", "empty"],
["exit", "empty", "trap"],
];
// Outer Label 'outer Naming Dungeon Cycles
'outer: for (row, floor) in maze.iter().enumerate() {
println!("Enter the dungeon, Floor #{}", row + 1);
for (col, room) in floor.iter().enumerate() {
match *room {
"exit" => {
println!(" Find the Exit!Location: ({}, {})", row, col);
found_exit = true;
break 'outer; // Exit two nested loops immediately
}
"key" => println!(" Get the keys (Location: {},{})", row, col),
"monster" => println!(" There's a monster!Skip this room"),
_ => println!(" Vacant Room {}", col),
}
}
}
println!("Have you found the exit?: {}", found_exit);
}
Rustのループラベル (
'outer) を使えば、breakどのレベルからでもループを抜け出すことができます。これはgotoよりも安全です。コード内の任意の場所にジャンプするのではなく、単にループから抜け出すだけだからです。
3. 3つのループの比較
graph TB
A[Rust Loop] --> B[loop: Infinite Loop]
A --> C[while: Conditional Loops]
A --> D[for: Set Iteration]
B --> E[Until break up to and including]
B --> F[Available break Return Value]
C --> G[Check the condition before each loop]
D --> H[Iterating Through a Set Automatically/Scope]
D --> I[Most Commonly Used,Safest]
| 特徴 | loop |
while |
for |
|---|---|---|---|
| 使用場面 | 終了条件が未定義 | 条件駆動型 | 集合または範囲を反復処理する場合 |
| 戻り値 | break value 値を返す場合がある |
値を返すことはできない | 値を返すことはできない |
| 無限ループ | ネイティブ | while true |
不向き |
| パフォーマンス | 最適(コンパイラがループすることを認識している) | 若干劣る(毎回条件をチェックする) | 最適(イテレータによって直接駆動される) |
| ユースケース | リトライ/ポーリング | 条件付き待機 | 配列/ベクトル/範囲の反復処理 |
(2) break と continue の比較
| キーワード | 機能 | 値を返すことができるか | タグとの併用 |
|---|---|---|---|
break |
現在のループを終了 | break value; (ループ内のみ) |
break 'label; |
continue |
このループの残りの処理をスキップ | できません | continue 'label; |
(3) スコープ式
| 表記 | 意味 | 端値を含むか | 例 |
|---|---|---|---|
start..end |
半開区間 | いいえ | 0..5 → 0, 1, 2, 3, 4 |
start..=end |
閉区間 | はい | 0..=5 → 0, 1, 2, 3, 4, 5 |
..end |
0 から終わりまで | いいえ | ..3 → 0、1、2 |
start.. |
最初から最後まで | — | 3.. → 3、4、5、… |
4. ループの例
(1) ▶ サンプル:loop を使った無限ループと break を使った戻り値 (難易度 ⭐)
// ============================================
// loop Loop:A simulation of the "Guess the Number" game"Let's try again"
// ============================================
fn main() {
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 5 {
break counter * 2; // break Can have a return value!
}
};
println!("Loop ran 5 times, break returned: {}", result);
// Real-World Scenarios:Keep trying until you succeed
let mut attempts = 0;
let success = loop {
attempts += 1;
if attempts >= 3 {
break true;
}
// Simulate a failed operation,Keep trying
};
println!("Retry {} Success on the next attempt: {}", attempts, success);
}
出力:
Loop ran 5 times, break returned: 10
Retry 3 Success on the next attempt: true
loopは、Rust で値を返すことができる唯一のループ構造です。戻り値は、追加の変数を用意することなく、breakキーワードの直後に記述できます。これは、「成功するまで試行する」というパターンにおいて非常に有用です。
(2) ▶ サンプル:while 条件付きループ (難易度 ⭐)
// ============================================
// while Loop:Countdown in Decreasing Order
// ============================================
fn main() {
let mut countdown = 5;
while countdown > 0 {
println!("Countdown: {}...", countdown);
countdown -= 1;
}
println!("Launch!");
}
出力:
Countdown: 5...
Countdown: 4...
Countdown: 3...
Countdown: 2...
Countdown: 1...
Launch!
while各反復の開始前に条件を確認します。条件がfalseの場合、ループ本体全体をスキップします。これは、「反復回数は不明だが、終了条件は分かっている」という状況に適しています。
(3) ▶ サンプル:for と範囲を使った反復処理(難易度 ⭐⭐)
// ============================================
// for Loop:Iteration Range,Arrays and Strings
// ============================================
fn main() {
// 1. Range Iteration: 0..5 is 0 to 4 (Excludes 5)
println!("-- Scope 0..5 --");
for i in 0..5 {
print!("{} ", i);
}
println!();
// 2. Closing Range:0..=5 Includes 5
println!("-- Scope 0..=5 --");
for i in 0..=5 {
print!("{} ", i);
}
println!();
// 3. Iterate through an array
let fruits = ["apple", "banana", "cherry"];
println!("-- List of Fruits --");
for fruit in &fruits {
println!("{}", fruit);
}
// 4. Indexed Iteration(enumerate)
println!("-- Indexed Iteration --");
for (index, fruit) in fruits.iter().enumerate() {
println!("Fruit #{}: {}", index, fruit);
}
}
出力:
-- Scope 0..5 --
0 1 2 3 4
-- Scope 0..=5 --
0 1 2 3 4 5
-- List of Fruits --
apple
banana
cherry
-- Indexed Iteration --
Fruit #0: apple
Fruit #1: banana
Fruit #2: cherry
forは、Rust でループを行う際に最も推奨される方法です。これにより、範囲外へのアクセスが防止され、反復処理が自動的に行われます。0..nは半開区間(n を除く)、0..=nは閉区間(n を含む)です。.enumerate()は、各要素にインデックスを割り当てます。
(4) ▶ サンプル:ループラベルとネストされたループ(難易度 ⭐⭐⭐)
// ============================================
// Loop Label:break 'label Breaking Out of Multiple Levels of Nesting
// ============================================
fn main() {
let matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
];
let mut found = 0;
// 'search Outer Loop for Tag Marking
'search: for (i, row) in matrix.iter().enumerate() {
for (j, &val) in row.iter().enumerate() {
if val == 5 {
println!("Find the Target 5 At the location ({}, {})", i, j);
found = val;
break 'search; // Exit two nested loops immediately!
}
}
}
println!("Values found: {}", found);
// continue It can also be used in conjunction with tags.
'outer: for x in 0..3 {
for y in 0..3 {
if x == y {
continue 'outer; // Skip the remaining inner layers,Continue to the next outer layer
}
println!("x={}, y={}", x, y);
}
}
}
出力:
Find the Target 5 At the location (1, 1)
Values found: 5
x=1, y=0
x=2, y=0
x=2, y=1
ラベルがない場合、
breakとcontinueは最も内側のループにのみ影響します。ラベル ('name) を追加することで、どのレベルのループでも制御できるようになります。これは、gotoよりも制御性の高い「抜け出し」の方法です。
(5) ▶ サンプル:総合演習 — FizzBuzz と九九(難易度 ⭐⭐)
// ============================================
// Comprehensive Cycle Practice:FizzBuzz + The 9×9 Multiplication Table
// ============================================
fn main() {
println!("=== FizzBuzz (1-30) ===");
for n in 1..=30 {
match (n % 3, n % 5) {
(0, 0) => print!("FizzBuzz "),
(0, _) => print!("Fizz "),
(_, 0) => print!("Buzz "),
_ => print!("{} ", n),
}
if n % 10 == 0 { println!(); }
}
println!();
println!("\n=== The 9×9 Multiplication Table ===");
for i in 1..=9 {
for j in 1..=i {
print!("{}×{}={:<4}", j, i, i * j);
}
println!();
}
println!("\n=== while let Simulated Read ===");
let mut queue = vec![10, 20, 30, 40, 0];
while let Some(item) = queue.pop() {
if item == 0 {
println!("Encountered a termination signal,Stop Processing");
break;
}
println!("Projects in Progress: {}", item);
}
println!("\n=== loop Retry Mode ===");
let attempts = try_connect(3);
println!("Connection Results: {}", attempts);
}
fn try_connect(max_retries: u32) -> bool {
let mut attempt = 0;
loop {
attempt += 1;
println!("Attempt #{} to connect...", attempt);
if attempt >= max_retries {
break true;
}
}
}
出力:
=== FizzBuzz (1-30) ===
1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz
11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz
Fizz 22 23 Fizz Buzz 26 Fizz 28 29 FizzBuzz
=== The 9×9 Multiplication Table ===
1×1=1
1×2=2 2×2=4
1×3=3 2×3=6 3×3=9
1×4=4 2×4=8 3×4=12 4×4=16
1×5=5 2×5=10 3×5=15 4×5=20 5×5=25
1×6=6 2×6=12 3×6=18 4×6=24 5×6=30 6×6=36
1×7=7 2×7=14 3×7=21 4×7=28 5×7=35 6×7=42 7×7=49
1×8=8 2×8=16 3×8=24 4×8=32 5×8=40 6×8=48 7×8=56 8×8=64
1×9=9 2×9=18 3×9=27 4×9=36 5×9=45 6×9=54 7×9=63 8×9=72 9×9=81
=== while let Simulated Read ===
Projects in Progress: 40
Projects in Progress: 30
Projects in Progress: 20
Projects in Progress: 10
Encountered a termination signal,Stop Processing
=== loop Retry Mode ===
Attempt #1 to connect...
Attempt #2 to connect...
Attempt #3 to connect...
Connection Results: true
FizzBuzzは、
matchのタプルパターンマッチングを使用することで、より洗練された実装が可能になります。九九の計算には、2段階のfor+ rangeが使用されます。while letは、「値が取得される限り継続する」というループパターンの処理に適しています。loop+breakは、リトライロジックに適しています。
❓ よくある質問
loop と while true の違いは何ですか?loop を使用するのが推奨されます。for i in 0..n と for i in 0..=n の違いは何ですか?.. は半開区間(nを除く)を表し、..= は閉区間(nを含む)を表します。into_iter を呼び出して要素を借用しますが、fruits.iter() はより明示的で分かりやすいです。break の戻り値はどのように使用しますか?loop 内の break ステートメントは、関数と同様に値を返すことができます:let x = loop { break 42; };。📖 まとめ
loop: 無限ループ;値を返すにはbreak valueを使用してくださいwhile: 条件付きループ。各反復の前に条件をチェックするfor: セット/範囲の反復処理 — 強く推奨- 範囲式:
0..5(5を除く)、0..=5(5を含む) - ループタグ
'labelを使用すると、どのレベルであってもループを終了またはスキップすることができます breakループを終了する;continueこの反復の残りをスキップする
📝 練習問題
- 難易度 ⭐:
forを使用して0..=10を順に処理し、偶数のみを出力してください(if i % 2 == 0を使用して確認します)。 - 難易度 ⭐⭐:
loopとbreakを使用して戻り値をシミュレートしてください。vec![1, 3, 5, 7, 9, 11] の中から 6 より大きい最初の値を見つけ、それを返します。見つからない場合は -1 を返します。 - 難易度 ⭐⭐⭐:2層構造の
forループを使用して、9×9の九九表を出力してください。各行は1x1=1 1x2=2 …の形式で表示されます。ループラベルを使って改行のロジックを制御する方法を考えてみてください。



