// dictionary
let-else文.
let-else文は、Option型のSomeやResult型の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型(!)を評価する、つまり必ず発散(return・break・panic!など)しなければなりません4。マッチに失敗したのにelseブロックの後へ処理が進んでしまうと、束縛されるはずだった変数が存在しないまま参照されることになるため、発散が言語レベルで強制されています。
fn main() {
let value: Option<i32> = None;
let Some(n) = value else {
println!("値がありません");
// エラー: E0308(elseブロックが発散せず、期待される`!`型と一致しない)
};
println!("{n}");
}Playgroundで開く補足
else ifにあたる記法はない
if letはelse if letで複数パターンを連鎖できますが、let-else文にelse ifにあたる記法はなく、elseブロックは1つだけです。複数パターンを順に試したい場合はmatch式を使います。
Footnotes
-
The Rust Reference: Statements - let statements —
elseブロックがない場合パターンはirrefutableでなければならず、elseブロックがある場合はrefutableでもよいと定義しています。 ↩ -
Announcing Rust 1.65.0 | Rust Blog — let-else文がこのバージョンで安定化されたと告知しています。 ↩
-
The Rust Programming Language: Staying on the “Happy Path” with let…else —
let-elseを使うと、if letのように2つの分岐で大きく異なる制御フローを持つことなく、関数本体の“happy path”にとどまれると説明しています。 ↩ -
The Rust Reference: Statements - let statements —
elseブロックは必ず発散し、never型を評価しなければならないと定義しています。 ↩
この辞書が使われているページ
backlinks 2