【入門編】Dartの「switch式」で実装するResult型を用いたエラーハンドリングの標準化 – Dart コア文法・オブジェクト指向・Null安全解析バイブル

こんにちは!Dartの奥深い世界へようこそ。
FlutterやDartでの開発に慣れてくると、「例外(Exception)」の扱いに少しモヤモヤすることはありませんか?

「通信エラーやパースエラーが起きたとき、うっかりキャッチし忘れてアプリがクラッシュしてしまった…」
「この関数を呼び出すとき、どんなエラーが返ってくるのか、シグネチャ(型)を見ただけでは分からない…」

そんな現場の悩みを一発で解決し、さらにDart 3の最強機能であるパターンマッチング(switch式)を使ってコードを劇的に美しく安全にする手法があるんです。

今回は、例外を「型システム」に閉じ込め、コンパイラの力を借りてエラーハンドリングを完全に義務化する「Result型パターン」を一緒にマスターしていきましょう!
ここをクリアすれば、あなたの書くDartコードの堅牢性は一気にプロフェッショナルレベルに到達しますよ。

—

1. なぜ「例外(Exception)」だけでは不十分なのか?

JavaやC#、そして従来のDartでは、エラーが発生したときに `throw` で例外を投げ、呼び出し元で `try-catch` するのが一般的でした。

// 従来の例外スタイル
int parseAge(String input) {
final number = int.tryParse(input);
if (number == null) {
// 例外を投げる(型シグネチャには現れない!)
throw FormatException(‘有効な数値ではありません’);
}
return number;
}

このアプローチの何が問題か分かりますか?
それは、「この関数がどんなエラーを投げるのか、呼び出し元がコード上から知る術がない(ドキュメントや実装を見に行かないと分からない)」という点です。さらに、`try-catch` を書き忘れても、コンパイラは何も警告してくれません。結果として、実行時エラー(クラッシュ)の温床になってしまいます。

解決策:エラーを「値」として扱う Result型

そこで登場するのが Result型(直和型 / Either型) です。
「処理が成功したときは値(Data)を返し、失敗したときはエラー(Error)を返す」という仕組みを、クラスと型として表現します。

イメージ図にすると、こんな感じです。

[ 処理の実行 ]
│
├─ 成功 ──> Success (中身: 正しいデータ)
└─ 失敗 ──> Failure (中身: エラー情報)

Dart 3の強力な機能を使えば、このResult型が驚くほどエレガントに実装できます。さっそく実際のコードを見てみましょう!

—

2. Dart 3で実装する「Result型」の標準コード

まずは、成功を表す `Success` と失敗を表す `Failure` を包み込む、密閉型クラス(Sealed class)ベースのResult型を定義します。

// sead classを使うことで、このファイルをまたいだ勝手なサブクラスの作成を防ぎます(網羅性の保証)
sealed class Result {
const Result();
}

// 成功時のデータ保持クラス
class Success extends Result {
final T value;
const Success(this.value);
}

// 失敗時のエラー保持クラス
class Failure extends Result {
final E error;
const Failure(this.error);
}

たったこれだけです!ジェネリクス(``)を使っているので、どんな成功データ型でも、どんなエラー型でも使い回せる汎用的な仕組みになっています。

関数のシグネチャを書き換えてみよう

先ほどの年齢パース処理を、この `Result` を返すように書き換えてみます。

// 戻り値が「Intを返す」のではなく「Result(成功ならint、失敗ならStringのエラーメッセージ)」を返すことを明示
Result parseAge(String input) {
final number = int.tryParse(input);
if (number == null) {
return const Failure(‘有効な数値ではありません’);
}
if (number < 0 || number > 150) {
return const Failure(‘年齢の範囲が不正です(0〜150)’);
}
return Success(number);
}

これなら、関数のシグネチャを見ただけで「あ、この関数は失敗する可能性があるな」と一目で分かりますよね。

—

3. 圧倒的進化!Dart 3の「switch式」でエラーハンドリングを強制する

さて、ここからが本番です。返ってきた `Result` 型を、Dart 3のswitch式を使って処理します。

従来の `switch-case` 文と違い、Dart 3の switch式(Expression) は値を返すことができ、さらにすべてのパターンを網羅しているか(Exhaustiveness checking)をコンパイラが厳密にチェックしてくれます。

void handleUserInput(String input) {
final result = parseAge(input);

// switch式の結果を変数に代入できる
final String message = switch (result) {
// 成功パターン
Success(value: final age) => ‘あなたの年齢は $age 歳ですね!’,

// 失敗パターン
Failure(error: final msg) => ‘エラーが発生しました: $msg’,

// ※もし sealed class に新しい状態を追加して処理を書き忘れると、
// コンパイルエラー(Exhaustiveness error)になるため、絶対にハンドリング漏れが起きません!
};

print(message);
}

ここがポイント!パターンマッチングの魔力

`Success(value: final age)` の部分に注目してください。
これは「`Success` 型のインスタンスであれば、その中の `value` プロパティを取り出して `age` というローカル変数にバインドする」という、Dart 3の強力なパターンマッチング構文です。

わざわざ `(result as Success).value` のような危なっかしいキャスト(型変換)をする必要は一切ありません。型安全かつエレガントに、中身を取り出すことができます。

—

4. 開発現場でやりがちな「文法エラー」と注意点

新しい構文を学ぶとき、誰しも一度はハマるポイントがありますよね。代表的なものをいくつかピックアップしておきます。

1. `switch` 文 と `switch` 式 の書き間違い

  • 文(Statement):従来の書き方。各ケースの末尾に `break` や `=>` が必要で、値を直接返せません。
  • 式(Expression):Dart 3の新しい書き方。`final result = switch (x) { … };` のように、値の評価結果を変数に代入したり、関数のreturnに直接書けます。末尾は `,`(カンマ)で区切ります。

// ❌ やりがちなミス(switch文のノリでセミコロンをつけてしまうなど)
final val = switch (result) {
Success(value: v) => v,
Failure(error: e) => 0, // 各アームの終わりはカンマ(,)!
}; // ここにセミコロン

2. 未網羅エラー(Exhaustiveness)の放置

もし `Result` クラスに、新しい状態(例えばローディング中を表す `Loading` など)を追加した際、すべての `switch` 式でその処理を書き忘れると、Dartのコンパイラが容赦なくエラーを吐きます。
「面倒だな」と感じるかもしれませんが、この厳格さこそが、大規模開発でバグをゼロにするための最大の武器になります。

—

5. まとめ:型で縛ることで、コードはもっと自由になる

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

  • 例外を投げるのではなく、Result型(Success / Failure)を戻り値として返すことで、エラーの発生を型に含める。
  • Dart 3の switch式とパターンマッチング を使い、安全かつ直感的に値を取り出す。
  • コンパイラの網羅性チェック(Exhaustiveness checking)により、エラー処理の書き忘れを完全に防止する。

「例外処理の書き忘れでアプリがクラッシュする恐怖」から解放されると、ビジネスロジックの構築に心置きなく集中できるようになります。
ぜひ、あなたの次のFlutterプロジェクトやDartコンソールアプリのカスタム層から、このResult型パターンを取り入れてみてくださいね。

ここをクリアしたあなたなら、もうDart 3のモダンな機能を使いこなすバッチリなエンジニアです!
それでは、また次回の記事でお会いしましょう。ハッピー・コーディング!

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