【Rust初心者向け】if letを完全理解!使い方とサンプルを紹介

2025年4月21日月曜日

Rust

Rustの「if let」、なんだかちょっと難しそう…?いえいえ、実はとっても便利な構文なんです!

Rustを書いていて、「`match`文、ちょっと長くなっちゃうなあ」と感じたことはありませんか?特に、`Option`型や`Result`型を扱うとき、特定のパターンにだけ用事がある場合って結構ありますよね。

この記事では、Rustの`if let`について、基本のキから実際の使い方、そしてどんな時に使うのがベストなのかを、初心者の方にも分かりやすいように解説していきます。

読み終わるころには、「なるほど、`if let`ってこう使うのか!」とスッキリしているはず。

この記事で学べること

  • `if let`がどんな機能なのか
  • なぜ`if let`が必要なのか(`match`との関係)
  • `if let`の基本的な書き方
  • `Option`型や`Result`型での`if let`の使い方
  • 特定の`enum`バリアントでの`if let`の使い方
  • `if let`と`match`の使い分けのコツ
  • `if let`に`else`を組み合わせる方法
  • `if let`を使う上でのちょっとした注意点

Rustのif letとは? ~ `match`の冗長さを解消 ~

`if let`は、一言でいうと`match`式をもっとシンプルに書くための便利な書き方です。

特に、たくさんのパターン(可能性)がある中で、たった1つのパターンにだけ注目したい場合に力を発揮します。

例えば、`match`だとこんな感じになることがありますよね。

let some_value: Option<i32> = Some(5);

match some_value {
    Some(number) => {
        println!("値が見つかりました: {}", number);
    }
    None => {
        // Noneの場合は何もしない…けど、書かないといけない
    }
}

上の例だと、`Some`の場合だけ処理したいのに、`None`の場合も`match`のルール上、書く必要があります。ちょっとだけ、もどかしい感じがしませんか?

`if let`を使うと、注目したいパターンだけをスッキリ書けるようになりますよ!

なぜRustには`if let`が必要なのか?

先ほどの`match`の例のように、Rustでは`Option<T>`(値があるかもしれないし、ないかもしれない)や`Result<T, E>`(成功したかもしれないし、エラーかもしれない)のような型をよく使います。

これらの型を`match`で扱うとき、関心があるのは片方のパターンだけ、という場面は少なくありません。

  • `Option`型なら、「`Some`の時だけ中の値を使いたい」(`None`は何もしない)
  • `Result`型なら、「`Ok`の時だけ成功した値を使いたい」(`Err`は後でまとめて処理するか、今は無視)

こういう時に`match`を使うと、関心のないパターンに対しても `_ => {}` のような、いわゆる「何もしない」処理を書く必要が出てきて、コードが少しだけ長くなってしまいます。

そこで登場したのが`if let`です!`if let`は、「もしこのパターンにマッチしたら、中の処理を実行してね」という、まさにそんな要望に応えるために用意された構文なんです。

コードの見た目をスッキリさせ、読みやすくする目的があるんですね。

Rustの`if let`の基本的な書き方

`if let`の基本的な形は、以下のようになります。

if let パターン = 式 {
    // パターンにマッチした場合に実行される処理
}

それぞれの部分を見ていきましょう。

  • `if let` - 「もしパターンにマッチしたら」という始まりの合図です。
  • パターン - マッチさせたい具体的な形を書きます。`Some(v)` や `Ok(x)` 、 `Color::Red` など、`match`の腕(アーム)の左側に書くようなものをイメージしてください。
  • `=` - パターンと式を結びつけます。
  • - パターンと比較したい値や、評価すると値になるものを書きます。`Option`型の変数や、関数呼び出しの結果などがここに来ることが多いでしょう。
  • `{ ... }` - パターンが見事にマッチした場合にだけ実行されるコードブロックです。

簡単な例を見てみましょう。

let favorite_color: Option<&str> = None;

if let Some(color) = favorite_color {
    println!("好きな色は {} です!", color);
} else {
    println!("好きな色はないみたいです。"); // これは後で説明するelse節
}

// 上記を実行すると、favorite_colorはNoneなので、elseブロックが実行される
// 出力結果:
// 好きな色はないみたいです。

この例では、`favorite_color`(式)の中身が `Some(color)`(パターン)にマッチするかどうかをチェックしています。今回は`None`なのでマッチせず、`println!`は実行されません(もし`else`があればそちらが実行されます)。

Rustの`if let`の使い方 - 実践的なコード例

それでは、実際に`if let`がどのような場面で役立つのか、実際のコード例を見ていきましょう。よく使われるパターンをいくつか紹介しますね!

`Option<T>`型を簡潔に扱う

Rustプログラミングで避けては通れない`Option<T>`型。値が存在する`Some(v)`の場合だけ何かしたい、という時に`if let`はぴったりです。

例えば、こんな関数があったとします。

fn get_nickname(name: &str) -> Option<&str> {
    match name {
        "Taro" => Some("Tarouchan"),
        _ => None,
    }
}

fn main() {
    let name = "Taro";
    let nickname_option = get_nickname(name);

    // --- matchを使う場合 ---
    match nickname_option {
        Some(nickname) => {
            println!("{}さんのニックネームは{}です。", name, nickname);
        }
        None => {
            // ニックネームがない場合は特に何もしない
            println!("{}さんにはニックネームがありません。", name);
        }
    }

    // --- if let を使う場合 ---
    if let Some(nickname) = nickname_option {
        println!("{}さんのニックネームは{}でした!(if let版)", name, nickname);
    }
    // Noneの場合は何もしないので、これで終わり!スッキリ!
}

表示結果

TaroさんのニックネームはTarouchanです。
TaroさんのニックネームはTarouchanでした!(if let版)

`match`を使う場合、`None`の時の処理も書く必要がありますが、`if let`を使えば`Some`パターンにマッチした時の処理だけを書けばOKです。

`Some`から値を取り出す手軽さが魅力ですね。コードが短くなり、意図も明確になります。

`Result<T, E>`型の成功パターンを処理する

関数の実行結果を表す`Result<T, E>`型も、`if let`が活躍する場面が多いです。特に、処理が成功した`Ok(v)`の場合だけ、その値を使いたい時です。

文字列を数値に変換する例を見てみましょう。

fn main() {
    let number_str = "123";
    let parse_result = number_str.parse::<i32>(); // これは Result<i32, ParseIntError> を返す

    // --- matchを使う場合 ---
    match parse_result {
        Ok(number) => {
            println!("数値への変換成功(match): {}", number);
        }
        Err(ref e) => { // ← ここを修正
            println!("数値への変換失敗(match): {:?}", e);
        }
    }

    // --- if let を使う場合 ---
    if let Ok(number) = parse_result {
        println!("数値への変換成功(if let): {}", number);
        // 成功した時の処理だけ書けばいい
    } else {
        // 失敗した場合の処理は else でまとめて書ける(後述)
        println!("数値への変換失敗(if let/else)");
    }
}

表示結果

数値への変換成功(match): 123
数値への変換成功(if let): 123

`.parse()`メソッドは`Result`を返します。`if let Ok(number) = ...` と書くことで、成功した(`Ok`)時だけ中の値`number`を取り出して処理できます。

`Err`の場合は`if let`のブロックはスキップされます。エラー処理は後でまとめて行うか、`else`節を使えば、よりシンプルに記述できます。

特定のenumバリアントのみを処理する

自分で定義した`enum`(列挙型)で、特定のバリアント(種類)の時だけ処理を実行したい場合も`if let`が便利です。

簡単なメッセージを表す`enum`を考えてみましょう。

enum Message {
    Quit,
    Write(String),
    Move { x: i32, y: i32 },
}

fn main() {
    let msg = Message::Write(String::from("こんにちは!"));

    // --- matchを使う場合 ---
    match msg {
        Message::Quit => println!("終了します(match)"),
        Message::Write(ref text) => { // ← ref を追加して参照にする
            println!("書き込みメッセージ(match): {}", text);
        }
        Message::Move { x, y } => {
            println!("移動します(match): x={}, y={}", x, y);
        }
    }

    // --- if let を使う場合 (Write の場合だけ処理したい) ---
    if let Message::Write(text) = msg {
         println!("書き込みメッセージ(if let): {}", text);
    }
    // QuitやMoveの場合は何もしないので、これでOK
}

表示結果

書き込みメッセージ(match): こんにちは!
書き込みメッセージ(if let): こんにちは!

この例では、`Message`が`Write`バリアントの場合だけ、中の`String`を取り出して表示したい、と考えています。

`match`だと`Quit`や`Move`の場合も書く必要がありますが、`if let`なら`Message::Write(text)`のパターンだけを指定して、マッチした場合の処理を書けます。関心のある種類だけをスマートに扱えるのが良い点です。

Rustの`if let` と `match` の明確な使い分けガイド

ここまで見てきて、「じゃあ、`if let`と`match`、どっちを使えばいいの?」と思ったかもしれません。使い分けのポイントはシンプルです!

  • `if let` が向いている時
    • 特定の1つのパターンにだけ関心がある時。
    • それ以外のパターンは無視するか、`else`でまとめて処理したい時。
    • コードをできるだけ簡潔に書きたい時。
  • `match` が向いている時
    • すべてのパターンを網羅的にチェックしたい時(Rustコンパイラが漏れをチェックしてくれます!)。
    • 複数のパターンに対して、それぞれ異なる処理を行いたい時。
    • 処理の分岐が複雑になる時。

基本的には、「1つのパターンだけ見たいなら`if let`、全部のパターンをしっかり見たいなら`match`」と考えると分かりやすいでしょう。

どちらが絶対的に良いというわけではなく、状況に応じて適切な方を選ぶのが、読みやすいコードを書くコツですよ。

`else`節を伴う`if let`の使い方

`if let`は、普通の`if`文と同じように`else`節と組み合わせることもできます!

if let パターン = 式 {
    // パターンにマッチした場合の処理
} else {
    // パターンにマッチしなかった場合の処理
}

これは、「もしパターンにマッチしたらAの処理、そうでなければBの処理」という、お馴染みの条件分岐を実現できます。

先ほどの`Option`の例に`else`を追加してみましょう。

fn main() {
    let maybe_number: Option<i32> = None; // Some(10) とかにも変えて試してみてね

    if let Some(number) = maybe_number {
        println!("数値が見つかりました: {}", number);
    } else {
        println!("数値は見つかりませんでした。");
    }
}

ソースコードの表示結果 (maybe_numberがNoneの場合)

数値は見つかりませんでした。

`maybe_number`が`Some(値)`であれば`if let`ブロックが実行され、`None`であれば`else`ブロックが実行されます。単純な「あるかないか」の分岐なら、`match`よりも`if let ... else ...`の方がスッキリ書けることが多いでしょう。

さらに、`else if let` のように繋げることも可能ですが、あまりに複雑になるようなら`match`を使った方が読みやすい場合もあります。

Rustの`if let` を使う際の注意点

`if let`はとても便利ですが、使う上でいくつか知っておきたい点があります。

網羅性のチェックはない

`match`はすべてのパターンを網羅しているかコンパイラがチェックしてくれますが、`if let`は指定したパターンしか見ません。

他のパターンが存在することを忘れてしまう可能性があるので、注意が必要です。「本当に他のパターンは無視して大丈夫かな?」と一度考えてみると良いでしょう。

変数のスコープ

`if let Some(v) = ...`のようにパターン内で新しい変数(この例では`v`)を束縛した場合、その変数が使えるのは`if let`のブロック(`{}`の中)だけです。ブロックの外では使えません。これは通常の`if`文などと同じですね。

複雑なパターン

`if let`でも、タプルや構造体を使った少し複雑なパターンマッチも可能です。しかし、パターンが複雑になりすぎると、かえってコードが読みにくくなることも。

そんな時は、素直に`match`を使う方が良いかもしれません。

これらの点を頭の片隅に置いておくと、`if let`をより効果的に、そして安全に使うことができるはずです!

【まとめ】Rustの`if let`を理解してコードをシンプルに!

今回はRustの便利な構文`if let`について、その基本から使い方、`match`との違いまでを見てきました。

最後に、`if let`のポイントをまとめておきましょう。

  • `if let`は、特定の1つのパターンにマッチするかどうかをチェックする構文。
  • `match`で`_ => {}`を書くような場面で、コードを簡潔にできる。
  • `Option`, `Result`, `enum`など、特定の状態だけを扱いたい時に特に便利
  • `else`と組み合わせて、マッチしなかった場合の処理も書ける。
  • 「1つだけ見るなら`if let`、全部見るなら`match`」が使い分けの基本。
  • `match`のような網羅性チェックはない点に注意。

どうでしょう?`if let`が身近なものに感じられるようになったでしょうか?

最初は少し戸惑うかもしれませんが、実際にコードを書いて使っていくうちに、その便利さがきっと実感できるはずです。ぜひ、あなたのRustコードにも`if let`を取り入れて、よりシンプルで読みやすいプログラムを目指してくださいね!

【関連記事】
Rustとは?いま学ぶべき理由と特徴や始め方を詳しく解説

このブログを検索

  • ()

自己紹介

自分の写真
リモートワークでエンジニア兼Webディレクターとして活動しています。プログラミングやAIなど、日々の業務や学びの中で得た知識や気づきをわかりやすく発信し、これからITスキルを身につけたい人にも役立つ情報をお届けします。 note → https://note.com/yurufuri X → https://x.com/mnao111

QooQ