[Rustのはじめ方] Part7: 制御構文(if, match, loop, while, for)

Rust

プログラムの流れをコントロールしよう!

こんにちは!Rustの学習、順調に進んでいますか?😊 今回は、プログラムの流れを制御するための重要な要素、「制御構文」について学びます。 条件によって処理を変えたり、同じ処理を繰り返したりするときに使うのが制御構文です。Rustにはいくつかの種類があるので、ひとつずつ見ていきましょう!

1. if 式:条件で分かれる道 🤔

if 式は、最も基本的な条件分岐です。「もし○○なら△△する、そうでなければ□□する」という流れを作ります。Rustの if は他の多くの言語と似ていますが、条件式を () で囲む必要はありません。


fn main() {
    let number = 6;

    if number % 4 == 0 {
        println!("number is divisible by 4"); // 数値は4で割り切れます
    } else if number % 3 == 0 {
        println!("number is divisible by 3"); // 数値は3で割り切れます
    } else if number % 2 == 0 {
        println!("number is divisible by 2"); // 数値は2で割り切れます
    } else {
        println!("number is not divisible by 4, 3, or 2"); // 数値は4、3、2で割り切れません
    }
}
        
💡 Point: Rustの if は「式 (expression)」です。これは、if が値を返すことができる、ということです!そのため、let 文の右辺にも書けます。ただし、各ブロックで返す値の型は同じである必要があります。

fn main() {
    let condition = true;
    let number = if condition {
        5 // conditionがtrueなら5
    } else {
        6 // conditionがfalseなら6
    }; // if式の結果がnumberに束縛される

    println!("The value of number is: {}", number); // 値は5です
}
        

複数の条件がある場合は else if を繋げることができます。ただし、else if が多くなりすぎる場合は、次に紹介する match を使う方がコードがすっきりすることがあります。

2. match 式:強力なパターンマッチング ✨

match 式は、特定の値やパターンに基づいて処理を分岐させる、Rustの非常に強力な機能です。if 式よりも複雑な条件分岐や、特定の値に対する処理を記述するのに適しています。他の言語の switch 文に似ていますが、より高機能です。

match は、与えられた値(または式の結果)が、各「アーム (arm)」のパターンに一致するかどうかを上から順に調べます。最初に一致したアームのコードが実行されます。


fn main() {
    let x = 5;

    match x {
        1 => println!("one"), // 値が1の場合
        2 => println!("two"), // 値が2の場合
        3 | 4 | 5 => println!("three, four, or five"), // 値が3, 4, 5のいずれかの場合 (`|` でOR条件)
        6..=10 => println!("six through ten"), // 値が6から10の範囲の場合 (`..=` で含む範囲)
        _ => println!("something else"), // 上記のいずれにも一致しない場合 (`_` はワイルドカード)
    }
}
        
⚠️ 重要: match 式は網羅的 (exhaustive) でなければなりません。つまり、取りうる全ての可能性をカバーするパターンを記述する必要があります。もしパターンが不足していると、コンパイルエラーになります。これは、バグを防ぐためのRustの安全機能の一つです。多くの場合、_(ワイルドカード)を使って「それ以外全て」の場合を処理します。

Option 型や Result 型(これらは後のステップで詳しく学びます)を扱う際にも match は非常によく使われます。


fn main() {
    let result: Result<i32, &str> = Ok(10); // 成功した場合の値

    match result {
        Ok(value) => println!("Success! Value is {}", value), // Okバリアントにマッチし、中の値を取り出す
        Err(error) => println!("Error: {}", error),        // Errバリアントにマッチし、中のエラー情報を取り出す
    }
}
        

3. ループ:繰り返し処理 🔄

同じような処理を何度も繰り返したい場合があります。Rustには3種類のループ構文があります。

3.1. loop 式:無限ループと脱出

loop は、明示的に停止させるまで無限に処理を繰り返します。ループから抜け出すには break キーワードを使います。


fn main() {
    let mut counter = 0;

    loop {
        counter += 1;
        println!("Again! Counter: {}", counter);

        if counter == 5 {
            break; // counterが5になったらループを抜ける
        }
    }
    println!("Exited the loop.");
}
        
💡 Point: loop も式であり、break 時に値を返すことができます!これにより、ループの処理結果を変数に束縛できます。

fn main() {
    let mut counter = 0;

    let result = loop {
        counter += 1;
        if counter == 10 {
            break counter * 2; // ループを抜け、counter * 2 の値を返す
        }
    };

    println!("The result is {}", result); // 結果は20です
}
        

ループ内で現在のイテレーション(繰り返し処理)をスキップして次のイテレーションに進みたい場合は continue キーワードを使います。


fn main() {
    let mut number = 0;
    loop {
        number += 1;
        if number % 2 == 0 {
            continue; // 偶数の場合は下のprintln!をスキップして次のループへ
        }
        println!("Odd number: {}", number); // 奇数のみ表示される
        if number >= 5 {
            break; // 5以上になったらループ終了
        }
    }
}
        

ネストしたループ(ループの中にループがある状態)で、内側のループから外側のループを抜けたい場合は、ループラベルを使うことができます。


fn main() {
    'outer: loop { // 外側のループに 'outer というラベルを付ける
        println!("Entered the outer loop");
        'inner: loop {
            println!("Entered the inner loop");
            // break; // ここで break すると内側のループだけ抜ける
            break 'outer; // 'outer ラベルが付いたループを抜ける
        }
        println!("This point will never be reached"); // break 'outer により実行されない
    }
    println!("Exited the outer loop");
}
        

3.2. while ループ:条件が真の間だけ繰り返す

while ループは、指定した条件が true である間、処理を繰り返します。条件が最初に評価され、false になったらループは終了します。while 式は値を返しません(常にユニット型 () を返します)。


fn main() {
    let mut number = 3;

    while number != 0 { // numberが0でない間繰り返す
        println!("{}!", number);
        number -= 1;
    }

    println!("LIFTOFF!!!"); // 発射!!!
}
        

while let という構文もあり、これは Option 型などが Some である間だけループを続ける、といったパターンで役立ちます(詳細は後のステップで)。

3.3. for ループ:コレクションの要素を反復処理

for ループは、配列、ベクタ、範囲(Range)などの「イテレータ (Iterator)」から要素を一つずつ取り出して処理するのに最もよく使われる、安全で簡潔なループです。イテレータについては後のステップで詳しく学びますが、ここでは基本的な使い方を見てみましょう。


fn main() {
    let a = [10, 20, 30, 40, 50]; // 配列

    // 配列aの各要素をelementとして取り出してループ
    for element in a {
        println!("the value is: {}", element); // 値は: element
    }

    // 1から4未満の範囲 (1, 2, 3) を逆順 (rev()) でループ
    for number in (1..4).rev() {
        println!("{}!", number);
    }
    println!("LIFTOFF!!!"); // 発射!!!
}
        

for ループは、指定した範囲やコレクションの全ての要素に対して処理を行うことが保証されるため、while ループでカウンター変数を使って要素にアクセスするよりも安全で間違いが少ない方法です。

まとめ 📜

今回はRustの基本的な制御構文を見てきました。

  • if 式: 条件に基づいて処理を分岐します。式なので値を返すことができます。
  • match 式: 強力なパターンマッチングで、複雑な分岐や網羅的なチェックを行います。
  • loop 式: 無限ループを作成し、break で脱出したり値を返したりできます。continue で次のイテレーションへスキップできます。
  • while ループ: 条件が真の間、繰り返し処理を行います。
  • for ループ: イテレータ(配列、範囲など)の要素を順に処理するのに最適で、安全です。

これらの制御構文を使いこなすことで、より複雑で実用的なプログラムを作成できるようになります。それぞれの構文の特徴を理解し、状況に応じて適切なものを選択しましょう!💪

次のステップでは、「関数定義と戻り値」について学びます。お楽しみに!🚀

コメント

タイトルとURLをコピーしました