【入門編】Dart 3のパターンマッチングにおける「失敗」の伝播:if-caseとswitch式の挙動の違い – Dart コア文法・オブジェクト指向・Null安全解析バイブル

こんにちは!Dartの奥深い世界へようこそ。
FlutterやDartのコードを書いていると、「もっとスマートに条件分岐を書きたい」「データの構造をそのままスッキリ取り出したい」と感じる瞬間はありませんよね。

Dart 3で導入されたパターンマッチングは、まさにその願いを叶えてくれる強力な武器です。
今回は、その中でも特に重要な「パターンの不一致(失敗)が起きたときに、制御フローがどう伝播するか」に焦点を当てて、基礎から本質までを一緒に紐解いていきましょう。

ここをクリアすれば、Dartの制御構文の解像度がグッと上がり、コードの安全性が劇的に跳ね上がりますよ。それでは、いってみましょう!

—

1. Dart 3のパターンマッチングにおける「失敗(Failure)」とは?

他の言語(RustやScalaなど)を触ったことがある方なら馴染みがあるかもしれませんが、パターンマッチングにおける「失敗」とは、「書いたパターンと、検査した値の構造や型が一致しなかった状態」を指します。

例えば、「ここに `(String, int)` 型のペアがあるはずだ」と思ってパターンを書いたのに、実際には `(String, String)` が渡されてきたら……。これは「パターンマッチの失敗」になります。

Dart 3では、この「失敗」に直面したとき、「その制御構文がどう振る舞うか」が構文ごとに異なります。今回は代表選手である `if-case` と ソリッドな `switch`式 の2つを取り上げ、その挙動の違いを徹底的に見ていきましょう。

—

2. 優しいお兄さん的アプローチ:`if-case` の世界

まずは、普段の `if` 文の感覚に最も近い `if-case` からです。
`if-case` は、「もしこのパターンに一致したら(成功したら)処理を実行して、一致しなかったら(失敗したら)何もしないか、`else` に流すよ」という非常に優しい構文です。

基本の使い方と挙動

以下のコードを見てください。APIから受け取ったJSONのような動的なデータ(`Map`)を安全に取り出すシチュエーションを想像してくださいね。

void processResponse(Map json) {
// if-case を使ったパターンマッチング
if-case json 構文ではない点に注意! ‘if (json case …)’ です。
if (json case {‘status’: ‘success’, ‘data’: String message}) {
print(‘成功しました! メッセージ: $message’);
} else {
print(‘失敗または予期せぬデータ構造です。’);
}
}

void main() {
// パターンに一致するケース(成功)
processResponse({‘status’: ‘success’, ‘data’: ‘データを取得できました’});
// 出力: 成功しました! メッセージ: データを取得できました

// パターンに一致しないケース(失敗)
processResponse({‘status’: ‘error’, ‘code’: 404});
// 出力: 失敗または予期せぬデータ構造です。
}

`if-case` での「失敗」の伝播

`if-case` の世界では、パターンマッチが失敗してもプログラムはクラッシュしません。
単にその `if` ブロックの中がスキップされ、`else` ブロック(あれば)に制御が流れるか、そのまま何事もなかったかのように次の行へ進むだけです。

「条件に合えばラッキー、合わなければスルーしてね」という、非常に安全で扱いやすい挙動になっています。

—

3. 厳格な審判:`switch` 式(Switch Expressions)の世界

続いては、Dart 3で劇的にパワーアップした `switch`式 です。従来の文(Statement)としての `switch` とは異なり、値を返す「式(Expression)」として機能します。

ここで非常に重要なポイントがあります。
「`switch`式は、すべての可能なケース(網羅性:Exhaustiveness)をカバーしていなければならない」 という鉄則です。

基本の使い方と挙動

先ほどと同じデータを、今度は `switch`式で処理してみましょう。

String getResponseMessage(Map json) {
return switch (json) {
// パターン1: 成功ケース
{‘status’: ‘success’, ‘data’: String message} => ‘成功: $message’,

// パターン2: エラーケース
{‘status’: ‘error’, ‘code’: int errorCode} => ‘エラーコード: $errorCode’,

// パターン3: どれにも一致しない場合の「デフォルト(ワイルドカード)」
_ => ‘未知のレスポンスです’,
};
}

void main() {
print(getResponseMessage({‘status’: ‘error’, ‘code’: 500}));
// 出力: エラーコード: 500

print(getResponseMessage({‘status’: ‘unknown’}));
// 出力: 未知のレスポンスです
}

`switch`式における「失敗」と例外の罠

もし、上記のコードから最後の `_ => …`(ワイルドカードパターン:すべてを受け止める網)を取り除いたらどうなるでしょうか?

実は、コンパイルエラーになります。
Dartのコンパイラ(Dart VM / AOTコンパイラ)は、「この `switch`式に渡りうるすべてのデータ構造(型や値)のうち、どのパターンにも一致しないケース(失敗する可能性)が残っているよ!」と厳しく検知します。

もし、仮に網羅性が強制されない文脈や、実行時にどのパターンにもマッチしなかった場合(例えば、言語の仕様の隙間や動的な型キャストの絡みなど)、`switch`式は `SwitchCaseNotFoundException`(またはそれに類するStateError)という実行時例外をスローしてクラッシュします。

`if-case` が「不一致ならスルー」するのに対し、`switch`式は「すべての可能性を網羅していない(=パターンマッチが失敗する余地を残すこと)をコンパイル時に許さない」という、極めて厳格なアプローチを取るのです。

—

4. 陥りやすい文法エラーと「型チェック」の罠

ここで、初学者の開発現場で本当によくある「ハマりどころ」をひとつご紹介します。

「あれ、変数の型が絞り込まれない!?」

パターンマッチの最中で、変数の型をチェックしつつ取り出そうとして、こんなコードを書いたことはありませんか?

Object obj = ‘Hello Dart 3’;

// やりがちなミス(※これはコンパイルエラーまたは意図しない挙動になります)
if (obj case String s && s.length > 5) {
print(‘長い文字列です: $s’);
}

Dart 3のパターン内では、論理演算子として `&&`(ガード句の代わりや条件の結合)が使えますが、パターンの構文規則を厳密に守る必要があります。
特に、ガード条件を書きたい場合は、`if-case` の後ろに `when` 句を使うのがDartの正しいイディオムです。

正しいイディオム:`when` 句の活用

Object obj = ‘Hello Dart 3’;

if (obj case String s when s.length > 5) {
// パッチが成功し、かつ when の条件(ガード)が真のときだけここに入る
print(‘条件クリア! 長い文字列です: $s’);
} else {
print(‘パターン不一致、または長さが短すぎます。’);
}

このように、パターンマッチ自体の構造不一致(失敗)だけでなく、追加の条件(ガード)による失敗も考慮することで、バグの入り込む隙間を完全にシャットアウトできます。

—

5. まとめ:制御フローの選択基準

ここまでの内容を、頭の中でスッキリ整理してみましょう。

| 構文 | パターン不一致(失敗)時の挙動 | 網羅性のチェック | 主なユースケース |
| :— | :— | :— | :— |
| `if-case` | マッチしなくてもスルー(安全に次へ / elseへ) | なし(部分一致でOK) | 特定のデータ構造だけを安全につまみ食いしたいとき |
| `switch`式 | すべて網羅すべき。漏れがあるとコンパイルエラー(実行時は例外) | 必須(全パターン網羅) | 状態(State)やレスポンス型など、すべての分岐を網羅的に処理したいとき |

Dart 3のパターンマッチングは、単なる「便利なシンタックスシュガー」ではありません。
「コンパイラにコードの安全性を証明させるための強力な型システムの一部」なのです。

ここをクリアできれば、あなたの書くDartコードはより堅牢で、予測可能で、何より美しいものになります。ぜひ明日の開発から、`if-case` と `switch`式を適材適所で使い分けてみてくださいね。

それでは、また次回の深い知見の世界でお会いしましょう!バッチリマスターしていきましょう!

タイトルとURLをコピーしました