Rust制御フローをThe BookとAIで学ぶ!

Rust制御フローをThe BookとAIで学ぶ!

Rust制御フロー入門:if, loop, while, for を使いこなそう!

こんにちは!今日もRustのお勉強です。
引き続き、The Rust Programming Language 日本語版 (通称 “The Book”) を読み進めていきます。

今回は、The Bookの「制御フロー」の章を学びます。
(3.5. 制御フロー – The Rust Programming Language 日本語版)

制御フローは、プログラムの中で超重要な部分!
条件分岐 (if) や繰り返し (loop, while, for) を使いこなせるようになれば、プログラムらしいプログラムが書けるようになります!

Rustの制御フロー:分岐と繰り返し

プログラムの基本的な構造は、「順次」「分岐」「繰り返し」の3つで構成されています。
Rustの制御フローを学ぶことで、これらのうち「分岐」と「繰り返し」を扱えるようになります。

if 式:条件分岐

if 式は、「もし〜なら〇〇する、そうでなければ××する」というように、条件によって処理を分岐させたい時に使います。

if キーワードの後に条件式を書き、{} (中括弧) の中に、条件が真 (true) の場合に実行するコードを記述します。


fn main() {
    let number = 3;

    if number < 5 {
        println!("true");
    } else {
        println!("false");
    }
}

このコードでは、変数 number の値が5未満であれば “true”、そうでなければ “false” を出力します。
else ブロックは省略可能です。

ポイント:

  • 条件式は bool 型 (true または false) でなければなりません。
  • Rustでは、他の言語のように、数値などが自動的に論理値に変換されることはありません。

複数の条件で分岐させたい場合は、else if を使います。


fn main() {
    let number = 6;

    if number % 4 == 0 {
        println!("numberは4で割り切れます");
    } else if number % 3 == 0 {
        println!("numberは3で割り切れます");
    } else if number % 2 == 0 {
        println!("numberは2で割り切れます");
    } else {
        println!("numberは4,3,2で割り切れません");
    }
}

else if はいくつでも繋げることができますが、あまり多用するとコードが読みにくくなるので注意しましょう。

let 文の中で if 式を使うこともできます。


fn main() {
    let condition = true;
    let number = if condition { 5 } else { 6 };
    println!("numberの値は{}", number); // numberの値は5
}

この書き方を利用することで、例えばログイン状況によって表示ページを切り替える処理も
以下のように記述することができます。


fn main() {
    let login = false;
    let hyouji_page: String = if login {
        "mypage.html".to_string()
    } else {
        "guestpage.html".to_string()
    };
    println!("ログイン状況でページの表示を切り替えます: {}", hyouji_page);
}

解説:
login変数は、ログイン状態を表すブール値です。
hyouji_page変数は、if式の結果に基づいてページのファイル名を格納します。
if login { "mypage.html" } else { "guestpage.html" }:loginがtrueの場合は”mypage.html”が、falseの場合は”guestpage.html”がhyouji_pageに代入されます。
println!マクロは、hyouji_pageの値を出力します。

ただし、if 式の各アーム ({} の中の部分) の結果は、同じ型でなければなりません。


let number = if condition { 5 } else { "six" }; // コンパイルエラー

この例では、if アームが i32 型、else アームが &str 型となり、型が一致しないため、コンパイルエラーになります。

ループ (loop, while, for):繰り返し

Rustには、3種類の繰り返し処理 (ループ) があります。

  • loop: 無限ループ
  • while: 条件付きループ
  • for: コレクションの各要素に対するループ

loop キーワード

loop キーワードは、明示的に停止させるまで、同じコードを永遠に繰り返します。


fn main() {
    loop {
        println!("まだ");
    }
}

このプログラムは、Ctrl+C などで強制終了するまで、”まだ” と出力し続けます。

ループを途中で抜けるには、break キーワードを使います。
また、continue キーワードを使うと、その回のループの残りの処理をスキップして、次の回のループを開始します。

ループにはラベルをつけることができ、ラベルはシングルクォーテーションから始めます。
breakやcontinueでどのループを操作するか指定できます。


fn main() {
    let mut count = 0;
    'counting_up: loop { // 外側のループに'counting_upラベルを定義
        println!("count = {}", count);
        let mut remaining = 10;

        loop { // 内側のループ
            println!("remaining = {}", remaining);
            if remaining == 9 {
                break; // 内側のループを終了
            }
            if count == 2 {
                break 'counting_up; // 外側のループを終了
            }
            remaining -= 1;
        }
        count += 1;
    }
    println!("End count = {}", count);
}

外側のループには'counting_upというラベルがついていて、
break 'counting_up;は外側のループを終了させます。
ループラベルを利用することで複雑にネストされたloopから指定されたループ場所まで抜けることが出来るので、よりわかりやすくなりそうですね。
また、ループラベルは全てのループにつけることができ、名前付き識別子とすることが出来るそうです。
ループにラベルをつけることで自分以外が記述したコードを読む場面でも、「このループは何を行なっているループなのか」を明示することが出来るのは便利ですね。
また、ループラベルをつけたからといって使わなければならないということもないそうです。

while キーワード

while キーワードは、条件が true の間、コードを繰り返し実行します。


fn main() {
    let mut number = 3;

    while number != 0 {
        println!("{}!", number);

        number -= 1;
    }

    println!("LIFTOFF!!!");
}

このコードは、カウントダウンを行い、最後に “LIFTOFF!!!” と出力します。

for キーワード

for キーワードは、配列などのコレクションの各要素に対して、コードを繰り返し実行します。


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

    for element in a {
        println!("value is: {}", element);
    }
}

このコードは、配列 a の各要素を順番に出力します。

for ループは、安全性と簡潔さから、Rustで最もよく使われるループだそうです。

一定回数コードを繰り返したい場合は、標準ライブラリで提供されている Range 型と、それを逆順にする .rev() メソッドを使うことができます。


fn main() {
    for number in (1..4).rev() {
        println!("{}!", number); // 3, 2, 1
    }
    println!("LIFTOFF!!!");
}

この .rev() メソッドは、Range型が双方向イテレータである場合に利用できます。

双方向イテレータとは、
始点と終点が明確に定義されていること
両方向からの要素アクセスが可能なことです。
Range型(1..4)は、これらの条件を満たすため、.rev()メソッドを適用することができます。

今回の学習を終えて

今回は、The Bookの「制御フロー」の章を読み、if 式、loop, while, for ループについて学びました。

これらの制御フローを組み合わせることで、かなり複雑な処理も記述できるようになります。
The Bookには、練習問題として、

  • 温度を華氏と摂氏で変換する
  • フィボナッチ数列のn番目を計算する
  • クリスマスキャロルの定番、”The Twelve Days of Christmas”の歌詞を出力する

といったプログラムを作成することが推奨されていました。
簡単なコーディング問題に挑戦して、理解度を確認するのも良さそうですね。

次は、Rustの重要な概念である「所有権」について学習する予定です。
所有権は、Rustの安全性と効率性を支える根幹となる概念で、他の言語にはない独特なものらしいので、しっかり理解したいと思います。

Comments

No comments yet. Why don’t you start the discussion?

    コメントを残す

    メールアドレスが公開されることはありません。 が付いている欄は必須項目です