Dartパターンマッチングの深淵:`if-case`と`switch`式の「失敗」がコードの寿命を決める
Dart 3で導入されたパターンマッチングは、単なる糖衣構文ではない。これは言語仕様の根幹に関わる「静的解析と実行時のフロー制御の融合」だ。
我々Dartエンジニアが理解すべきは、「パターンがマッチしなかったとき、プログラムの実行コンテキストで何が起きるか」という点に尽きる。特に、`if-case`と`switch`式における「失敗の伝播」を混同することは、バグの温床であり、アーキテクチャの劣化を招く。
今日は、その差異をVM内部の挙動から紐解き、実務で戦える「堅牢な設計」を叩き込む。
—
1. 「無視」と「遮断」:制御構文のイデオロギー
まず、この2つの根本的な性質を定義しよう。
- `if-case` (制御フローとしての分岐): パターンマッチングは「ガード」として働く。マッチしなかった場合、それは「単なるスキップ」だ。実行フローは次の一行へ進む。
- `switch`式 (式としての評価): これは「値の変換」だ。マッチしないことは「不完全な定義(網羅性の欠如)」とみなされ、ランタイムで `SwitchExpressionError` が発生する。
なぜ `switch` 式の「失敗」は許されないのか
`switch`式は、結果を返すことを前提とした「式」である。Dartの健全な型システムにおいて、値が確定しない式は定義できない。もしマッチしない可能性があるなら、それはコンパイラが「網羅性(Exhaustiveness)」を強制する理由そのものだ。
—
2. 実務で直面する「失敗の伝播」:具体例
APIレスポンスのハンドリングを例に見てみよう。
// 良くない例:switch式で予期せぬレスポンスを握りつぶす設計
final status = response.statusCode;
final result = switch (status) {
200 => ‘Success’,
404 => ‘Not Found’,
// 500等のエラーが来た瞬間、SwitchExpressionErrorでアプリがクラッシュする
};
一方、`if-case`はどうか。
// 安全な例:if-caseによるプログレッシブなフィルタリング
if (response case {‘data’: final data, ‘status’: 200}) {
return process(data);
}
// マッチしなかった場合? ここでログを吐くか、デフォルト値を返すかのフォールバックが可能
logger.warning(“Unexpected response: $response”);
return DefaultValue();
—
3. パフォーマンスとVMの挙動:隠れたコスト
ここでアーキテクトとしての視点を提供する。Dart VMのAOTコンパイルにおいて、`switch`式は非常に効率的な「ジャンプテーブル」や「決定木」へと最適化される。これは、定数時間(O(1)〜O(log n))で分岐を決定できる非常に強力な構造だ。
対して、`if-case`を多用すると、それは個別の条件評価の積み重ね(線形探索)となり、条件が複雑になればなるほど、命令キャッシュの効率が落ちる可能性がある。
設計の指針:
1. 網羅性が担保できる有限の集合(Enumやsealed class)なら、迷わず `switch` 式を使え。
2. 動的なJSON解析や、条件が複雑で「マッチしなければ何もしなくていい(あるいは別のロジックへ流したい)」なら `if-case` を選べ。
—
4. プロダクションコード:守りの設計パターン
APIクライアントにおける「Result型」を用いたパターンマッチングのベストプラクティスを提示する。
sealed class RemoteResult
class Success
class Failure extends RemoteResult
// コンポーネント設計での活用
Widget build(BuildContext context) {
final state = fetchState();
// switch式で「状態の漏れ」をコンパイラに検知させる(設計の堅牢性)
return switch (state) {
Success(value: final data) => Text(data.title),
Failure(error: final err) => ErrorWidget(err),
// 状態が追加されたらここでコンパイルエラーになるため、メンテナンス性が極めて高い
};
}
—
結びに:なぜ「言語の重み」を知る必要があるのか
私がチーフアーキテクトとしてコードレビューをする際、もっとも重視するのは「その書き方が、将来の変更に対してどう応答するか」だ。
`switch`式は「変更が来たら必ずここを直せ」というコンパイラからの強いメッセージであり、`if-case`は「ここは柔軟なフィルタリングの場である」という宣言だ。
Dart 3のパターンマッチングを使いこなすということは、単に便利な構文を並べることではない。プログラムの「状態の遷移」を、コンパイラのガードレールの上でいかにエレガントに描くかという知的な営みなのだ。
君たちが書くその一行が、将来のバグを防ぎ、チームの時間を救う。その責任を胸に、今日も美しいコードを書いてほしい。