404 Not Found

404 Not Found


nginx

Rustのループと反復処理:`loop`、`while`、`for`の習得

Rustのループは、単なる「無限ループ」というだけでなく、最後の計算結果を「出力」します。

3種類のループにはそれぞれ独自の用途があります:loop 無限ループ(戻り値あり)、while 条件付きループ、for 集合の反復処理です。


1. 学習内容


2. 迷路探検家の物語

(1) 苦しみ:迷路で道に迷う

アレックスは迷路ゲームを開発しています。この迷路は、3層の入れ子構造になっています:

TEXT
Outer layer (Dungeon) -> Middle layer (Room) -> Inner layer (Treasure Chest)

プレイヤーは出口を見つけるためにどんどん奥へ進まなければならないが、コードを書くのが難しいと感じている:

「もし各ループにラベルを付けられれば、好きな場所にジャンプできるのに……」

(2) Rustにおけるループの解決策

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つのループの比較

100%
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) breakcontinue の比較

キーワード 機能 値を返すことができるか タグとの併用
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 を使った戻り値 (難易度 ⭐)

RUST
// ============================================
// 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);
}

出力:

TEXT
Loop ran 5 times, break returned: 10
Retry 3 Success on the next attempt: true

loop は、Rust で値を返すことができる唯一のループ構造です。戻り値は、追加の変数を用意することなく、break キーワードの直後に記述できます。これは、「成功するまで試行する」というパターンにおいて非常に有用です。


(2) ▶ サンプル:while 条件付きループ (難易度 ⭐)

RUST
// ============================================
// while Loop:Countdown in Decreasing Order
// ============================================

fn main() {
    let mut countdown = 5;

    while countdown > 0 {
        println!("Countdown: {}...", countdown);
        countdown -= 1;
    }

    println!("Launch!");
}

出力:

TEXT
Countdown: 5...
Countdown: 4...
Countdown: 3...
Countdown: 2...
Countdown: 1...
Launch!

while 各反復の開始前に条件を確認します。条件が false の場合、ループ本体全体をスキップします。これは、「反復回数は不明だが、終了条件は分かっている」という状況に適しています。


(3) ▶ サンプル:for と範囲を使った反復処理(難易度 ⭐⭐)

RUST
// ============================================
// 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);
    }
}

出力:

TEXT
-- 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) ▶ サンプル:ループラベルとネストされたループ(難易度 ⭐⭐⭐)

RUST
// ============================================
// 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);
        }
    }
}

出力:

TEXT
Find the Target 5 At the location (1, 1)
Values found: 5
x=1, y=0
x=2, y=0
x=2, y=1

ラベルがない場合、breakcontinue は最も内側のループにのみ影響します。ラベル ('name) を追加することで、どのレベルのループでも制御できるようになります。これは、goto よりも制御性の高い「抜け出し」の方法です。


(5) ▶ サンプル:総合演習 — FizzBuzz と九九(難易度 ⭐⭐)

RUST
// ============================================
// 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;
        }
    }
}

出力:

TEXT
=== 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は、リトライロジックに適しています。


❓ よくある質問

Q loopwhile true の違いは何ですか?
A Rust では loop を使用するのが推奨されます。
Q for i in 0..nfor i in 0..=n の違いは何ですか?
A .. は半開区間(nを除く)を表し、..= は閉区間(nを含む)を表します。
Q 配列を反復処理する場合、&fruits と fruits.iter() の違いは何ですか?
A &fruits は暗黙的に into_iter を呼び出して要素を借用しますが、fruits.iter() はより明示的で分かりやすいです。
Q break の戻り値はどのように使用しますか?
A loop 内の break ステートメントは、関数と同様に値を返すことができます:let x = loop { break 42; };
Q ネストされたループが多すぎる場合はどうすればよいですか?
A 内側のループを関数として抽出することを検討してください。

📖 まとめ


📝 練習問題

  1. 難易度 ⭐for を使用して 0..=10 を順に処理し、偶数のみを出力してください(if i % 2 == 0 を使用して確認します)。
  2. 難易度 ⭐⭐: loopbreak を使用して戻り値をシミュレートしてください。vec![1, 3, 5, 7, 9, 11] の中から 6 より大きい最初の値を見つけ、それを返します。見つからない場合は -1 を返します。
  3. 難易度 ⭐⭐⭐:2層構造の for ループを使用して、9×9の九九表を出力してください。各行は 1x1=1 1x2=2 … の形式で表示されます。ループラベルを使って改行のロジックを制御する方法を考えてみてください。
Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%