Progrust Library.

// dictionary

let-else文.

let-else文は、Option型SomeResult型Okのようなrefutable(マッチしない可能性がある)パターンにマッチしたら値を変数へ束縛し、マッチしなければelseブロックで処理を打ち切るletです1。通常のletは必ずマッチが成功するirrefutableなパターンしか書けませんが、elseブロックを付けることでrefutableなパターンも扱えるようになります。Rust 1.65から使える構文です2

struct Item {
    name: String,
    price: i32,
}

fn print_price(items: &[Item], name: &str) {
    let Some(item) = items.iter().find(|item| item.name == name) else {
        println!("{name}は見つかりませんでした");
        return;
    };
    println!("{}: {}円", item.name, item.price);
}

fn main() {
    let items = vec![
        Item { name: "りんご".to_string(), price: 120 },
        Item { name: "みかん".to_string(), price: 80 },
    ];
    print_price(&items, "りんご");
    print_price(&items, "ぶどう");
}
Playgroundで開く

if letとの比較

let-else文とif let式はどちらもパターンマッチと分岐を1つの構文にまとめますが、束縛した値を使える範囲が異なります。

書き方 elseの要否 束縛した値を使える範囲
if let 省略可(発散しなくてもよい) if letのブロック内だけ
let-else 必須(必ず発散) 以降、囲むブロックスコープの終わりまで

if letで束縛した値をブロックの外(関数の残り)でも使いたい場合は、if let式全体の値をletで受け取り、マッチしなかった側の分岐はreturnなどで抜けるという書き方になります。

if letで書いた場合
struct Item {
    name: String,
    price: i32,
}

fn print_price(items: &[Item], name: &str) {
    let item = if let Some(item) = items.iter().find(|item| item.name == name) {
        item
    } else {
        println!("{name}は見つかりませんでした");
        return;
    };
    println!("{}: {}円", item.name, item.price);
}

fn main() {
    let items = vec![Item { name: "りんご".to_string(), price: 120 }];
    print_price(&items, "りんご");
}
Playgroundで開く

let-else文はこれを1つの文にまとめ、マッチが成功したときの処理(“happy path”、「幸せな道」)を関数本体の主流にとどめたまま、失敗時のreturn式(早期リターン)だけをelse側に分離できます3

elseブロックの発散

elseブロックはnever型(!を評価する、つまり必ず発散(returnbreakpanic!など)しなければなりません4。マッチに失敗したのにelseブロックの後へ処理が進んでしまうと、束縛されるはずだった変数が存在しないまま参照されることになるため、発散が言語レベルで強制されています。

fn main() {
    let value: Option<i32> = None;
    let Some(n) = value else {
        println!("値がありません");
        // エラー: E0308(elseブロックが発散せず、期待される`!`型と一致しない)
    };
    println!("{n}");
}
Playgroundで開く

補足

else ifにあたる記法はない

if letelse if letで複数パターンを連鎖できますが、let-else文にelse ifにあたる記法はなく、elseブロックは1つだけです。複数パターンを順に試したい場合はmatch式を使います。

Footnotes

  1. The Rust Reference: Statements - let statementselseブロックがない場合パターンはirrefutableでなければならず、elseブロックがある場合はrefutableでもよいと定義しています。

  2. Announcing Rust 1.65.0 | Rust Blog — let-else文がこのバージョンで安定化されたと告知しています。

  3. The Rust Programming Language: Staying on the “Happy Path” with let…elselet-elseを使うと、if letのように2つの分岐で大きく異なる制御フローを持つことなく、関数本体の“happy path”にとどまれると説明しています。

  4. The Rust Reference: Statements - let statementselseブロックは必ず発散し、never型を評価しなければならないと定義しています。

この辞書が使われているページ

backlinks 2

  1. 辞書unwrap
  2. 動かして学ぶRustプログラミング問題集 › 第12章 OptionとResult