はじめに
Rust 1.88 (2025年) で if let chain が安定化されました。2024 edition のコードでは積極的に使えます。 && で if let 条件を連結できるようになり、ネストした if let を平らに書ける構文です。短い変更ですが、効果は大きく出ます。
何を解決するか
Option が複数あって、すべて Some だったときだけ何かしたい、という場面。素直に書くとネストになります。
fn first_premium(items: &[String]) -> Option<&str> { if let Some(first) = items.first() { if let Some(rest) = first.strip_prefix("premium-") { return Some(rest); } } None }
「最初の要素を取り出して、それが premium- で始まっていれば残りを返す」という処理ですが、 if let が2段ネストして本筋が見えにくいです。
if let chain での書き方
2024 edition / Rust 1.88 以降ならこう書けます。
fn first_premium(items: &[String]) -> Option<&str> { if let Some(first) = items.first() && let Some(rest) = first.strip_prefix("premium-") { return Some(rest); } None }
&& で let パターンを連結します。「最初の let が成功して、かつ次の let も成功する」が満たされたときだけ if 本体に入ります。
ポイントを整理します。
- 各
letで取り出した変数 (first,rest) は 後続の条件と if 本体の両方で使える - 普通の bool 条件と混ぜられる:
if let Some(x) = a && x > 0 && let Some(y) = b { ... } - 失敗したら全体の if は false 扱い
ネストが消えて、guard 条件が一直線に読めます。
let-else との使い分け
似た用途で let-else もあります。
fn first_premium_letelse(items: &[String]) -> Option<&str> { let Some(first) = items.first() else { return None; }; let Some(rest) = first.strip_prefix("premium-") else { return None; }; Some(rest) }
両者は使い分けがあります。
| 構文 | 向いている場面 |
|---|---|
if let chain |
「成功したらある処理、失敗したら何もしない」: 条件が満たされた時だけ実行する |
let-else |
「失敗したら関数を抜ける」: ガード句として使い、以降の処理を平らに続ける |
first_premium の例だと、「失敗時は None を返すだけ」なので let-else のほうが少しシンプルでしょう。一方、「成功したらこの if 本体に入って、入らなければ後続処理に進む」のような continue 的な用途では if let chain が自然です。
具体例を見ます。
fn process(events: &[Event]) { for event in events { if let Event::Click { target } = event && let Some(url) = target.url() && url.starts_with("http") { println!("opening {url}"); } // 失敗したら次の event に進む } } # enum Event { Click { target: Target } } # struct Target; # impl Target { fn url(&self) -> Option<&str> { None } }
ループの中で「3条件すべて満たしたら何かする」を1つの if で書けます。let-else でループから continue するより自然です。
bool 条件と混ぜる
if let chain は普通の bool 条件と混ぜられます。
fn handle(message: &Message, allowlist: &[String]) { if let Some(sender) = &message.sender && allowlist.contains(sender) && let Some(body) = &message.body && !body.is_empty() { println!("ok: {sender} -> {body}"); } } # struct Message { sender: Option<String>, body: Option<String> }
let の取り出しと、普通の比較条件 (allowlist.contains, is_empty) が混ざっても、左から右に評価されます。途中で false なら短絡します。
何が嬉しいのか
主な3点を挙げます。
- ネスト削減:
if letの2段3段ネストが消える - 意図が線形に読める: 「これとこれが、さらにこれが揃ったら」が左から右に並ぶ
- 副条件を挟みやすい:
letの合間に普通の bool 条件を入れられる
特に3は地味に便利で、「if let Some(x) と書いてから if x > 0 のために中で if を1個増やす」というパターンが消えます。
注意: 安定化は 1.88
if let chain が stable になったのは Rust 1.88 です。それより古い rustc ではコンパイルが通りません。Cargo.toml に rust-version = "1.88" 以上を書いていなければ、CI 用に最低でも 1.88 で動かす必要があります。
[package] edition = "2024" rust-version = "1.88" # if let chain を使うなら
2024 edition そのものは 1.85 から有効ですが、if let chain のために MSRV を 1.88 に上げる、という関係になります。
簡単に試す
Cargo.toml:
[package] name = "if-let-chain-demo" version = "0.1.0" edition = "2024" rust-version = "1.88"
src/main.rs:
fn main() { let items = vec![ "premium-coffee".to_string(), "tea".to_string(), ]; if let Some(first) = items.first() && let Some(rest) = first.strip_prefix("premium-") { println!("found premium: {rest}"); } else { println!("nothing premium"); } }
cargo run で found premium: coffee が出ます。
まとめ
if letchain は 2024 edition / Rust 1.88 で実用フェーズに入った&&でletパターンと bool 条件を連結できる- 取り出した変数は後続の条件と if 本体の両方で使える
let-elseは「失敗したら抜ける」、if letchain は「成功したら何かする」で使い分け- 使うときは MSRV を 1.88 以上に揃える
関連
- Rust 1.88 release notes (
if letchains 安定化): https://blog.rust-lang.org/releases/ - RFC
let_chains: https://rust-lang.github.io/rfcs/2497-if-let-chains.html - `let-else` で早期 return を素直に書く - じゃあ、おうちで学べる