【入門編】Dartの「switch式」で「Result型」の実装を簡略化する – Dart コア文法・オブジェクト指向・Null安全解析バイブル

こんにちは!FlutterやDartを使った開発を楽しんでいますか?
世界中のコードベースを見てきたチーフアーキテクトの視点から言わせてもらうと、Dart 3で導入されたパターンマッチングとswitch式は、言語の歴史における最大のパラダイムシフトの一つです。

特に、エラーハンドリングの常識を根底から変える「Result型(成功・失敗の表現)」との組み合わせは、あなたの書くコードを劇的に美しく、そして堅牢にしてくれます。

今回は、初学者の方や他言語(TypeScriptやKotlin、Rustなど)から来た方に向けて、Dart 3のswitch式を使ってResult型をエレガントに料理する方法を、基本から本質まで優しく紐解いていきますね。ここをクリアすれば、あなたのDartの基本はバッチリマスターできますよ!

—

1. そもそも「Result型」ってなぜ必要なの?

普段のプログラミングで、こんなコードを書いたことはありませんか?

// 従来のよくあるスタイル(例外ベース)
try {
final user = fetchUser();
// 処理が続く…
} catch (e) {
// エラーハンドリング
}

例外(Exception)は便利ですが、「どこでどんなエラーが投げるのか」がシグネチャ(関数の型定義)から見えにくく、うっかりキャッチし忘れるとそのままアプリがクラッシュしてしまいますよね。

そこで登場するのが `Result型` です。
Result型とは、「処理が成功した(Data)」か「失敗した(Error)」のどちらかの状態を必ず包み込む、いわば「カプセル」のようなものです。

Dart 3のレコード(Records)でResult型を即席で表現する

Dart 3では、わざわざ大げさなクラスを作らなくても、レコード(Records)という機能を使って軽量なResult型を表現できます。

// 成功時は (data: データの型, error: null)
// 失敗時は (data: null, error: エラーの型)
// …とするより、もっとスマートな方法があります!

Dart 3の強力な武器である「代数的データ型(Sealedクラス)」を組み合わせるのが、最も美しく型安全なアプローチです。早速見ていきましょう!

—

2. Sealedクラスとswitch式で作る「極上のResultパターン」

まずは、成功と失敗を型安全に表現する `Result` を定義します。

// 1. 成功と失敗の設計図(Sealedクラス)
// Dartのコンパイラは、sealedクラスのサブクラスを完全に把握します。
sealed class Result {}

class Success extends Result {
final T value;
Success(this.value);
}

class Failure extends Result {
final Exception error;
Failure(this.error);
}

この `Result` を返す関数(例えば、ユーザーデータを取得する非同期関数)を考えてみます。

Result fetchUserData(int userId) {
if (userId <= 0) { return Failure(Exception('無効なユーザーIDです')); } return Success('Alice (ID: $userId)'); } ---

3. 圧倒的な真骨頂!Dart 3の「switch式」による網羅的ハンドリング

さあ、ここからが本番です。受け取った `Result` を、Dart 3のswitch式(Switch Expression)を使って処理します。

従来の `switch文` と違い、switch「式」は値を返します。さらに、Dartのコンパイラは「すべてのパターンが網羅されているか」を静的にチェックしてくれます。

void main() {
final result = fetchUserData(1);

// 2. switch式で結果をエレガントにハンドリング
final message = switch (result) {
// 成功パターン:Successクラスのvalueを取り出す
Success(value: final data) => ‘ようこそ、$dataさん!’,

// 失敗パターン:Failureクラスのerrorを取り出す
Failure(error: final err) => ‘エラーが発生しました: ${err.runtimeType}’,
};

print(message);
}

このコードの何がスゴいのか?(Dart VMの裏側と知見)

1. 網羅性チェック(Exhaustiveness Checking)
もし `Result` に新しい状態(例えば `Loading` など)を追加し忘れたり、`Failure` の処理を書き忘れたりすると、コンパイルエラーになります。実行時エラーを未然に防ぐ、最強の安全装置です。
2. 変数束縛(Pattern Binding)
`Success(value: final data)` の部分で、カプセルの中身をパターンマッチングで直接変数 `data` にバインディング(抽出)しています。余計なキャスト(`as Success` など)が一切不要で、非常に高速かつ安全に動作します。

—

4. 陥りやすい文法エラーと注意ポイント

初学者の開発現場でよく見られる「ハマりどころ」をいくつかピックアップしておきますね。

① switch「文」と switch「式」の書き間違い

  • 文(Statement):最後に `;` がつかず、値を返さない(従来の書き方)。
  • 式(Expression):末尾に `;` が必要で、アロエロータ(`=>`)の右側の結果を変数に代入できる。

// ❌ やりがちなミス(switch文のノリで書き換えてしまう)
switch (result) {
Success(value: var d) => print(d), // 文脈によってはエラーになる
}

// ⭕ 正しいswitch式(変数に代入する形にする)
final res = switch (result) {
Success(value: var d) => d,
Failure(error: _) => ‘Default’,
};

② ワイルドカードパターン(`_`)の活用

エラーの詳細な中身が要らない場合は、ワイルドカードを使ってスッキリ書けます。

final statusMessage = switch (result) {
Success() => ‘成功!’,
Failure() => ‘失敗…’, // エラーオブジェクトを取り出さない場合はこれでOK
};

—

5. まとめ

今回は、Dart 3のswitch式とResult型を組み合わせた、モダンで堅牢なエラーハンドリングの手法を解説しました。

  • Result型を使うことで、例外に頼らず「成功か失敗か」を型として明確に表現できる。
  • Dart 3のswitch式を使えば、値の抽出と網羅的な分岐を安全かつ簡潔に記述できる。
  • コンパイラがパターン網羅性をチェックしてくれるため、保守性の高いコードが書ける。

ここをマスターすれば、あなたの書くDartコードは一気にプロダクションクオリティに跳ね上がります。ぜひ、今日の開発から取り入れてみてくださいね。
それでは、次の知見でお会いしましょう!バッチリ使いこなしていきましょう!

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