Dartの多値返却とNull安全:Record型で「安全な戻り値」を設計する極意
Dartの世界へようこそ。言語の深淵に触れる準備はいいですか?
今日は、Dart 3で導入された強力な機能である「Record型」と、Dartの心臓部である「Sound Null Safety(健全なNull安全)」がどう交差するのか、そのベストプラクティスを紐解いていきましょう。
多くの開発者が、関数から「成功した時の値」と「エラー情報」を同時に返したいとき、どう型を定義すべきか迷います。ここでNull安全の考え方を誤ると、ランタイムエラーの温床になります。しかし、Record型を正しく使えば、その悩みは魔法のように消え去ります。
—
1. なぜ「Record型」なのか?
かつて、複数の値を返すには`List`を使うか、専用の`class`を定義していました。しかし、`List`は型安全ではありませんし、わざわざクラスを作るのはコードの肥大化を招きます。
Record型は、「コンパイル時に構造が確定する、一時的なデータ構造」です。
// ユーザー名と年齢を返すシンプルな例
(String, int) getUserInfo() {
return (‘Alice’, 30);
}
この`(String, int)`という記述。これは単なるデータ構造ではなく、DartのVMが「この関数は必ずこの型のペアを返す」と厳密に解釈する「型としての実体」です。
—
2. Null安全と「どこをNullにするか」の最適解
さて、ここからが本題です。検索エンジンで見かけるコードは、しばしば「全てをNull許容にする」という安易な実装をしがちです。しかし、それではDartが提供する「Null安全の恩恵」をドブに捨てることになります。
NG:すべてNull許容にしてしまう例
// 失敗した時に何を返せばいいかわからず、とりあえず全部Nullに…
(String?, int?) fetchData() {
return (null, null); // これでは呼び出し側が毎回 null チェック地獄に陥ります
}
ベストプラクティス:型で「状態」を表現する
「成功したのか、失敗したのか」という状態自体をRecordの構造に組み込むのが、Dart流のスマートな解法です。
// 成功した場合は値を返し、失敗した場合はエラーメッセージを返す
// どちらか一方が必ず存在するように定義する
({String? data, String? error}) fetchData() {
bool success = false; // 仮のロジック
if (success) {
return (data: ‘Success!’, error: null);
} else {
return (data: null, error: ‘Connection Failed’);
}
}
void main() {
final result = fetchData();
// 分解(Destructuring)を使ってスマートに処理
final (data: data, error: error) = result;
if (error != null) {
print(‘エラー発生: $error’);
} else {
print(‘データ取得成功: $data’);
}
}
なぜこの書き方が「最強」なのか?
1. 名前付きフィールドを使用: `(String?, int?)`のような位置ベースのRecordではなく、`(data: …, error: …)`と名前を付けることで、呼び出し側で「何がエラーなのか」が明確になります。
2. Nullチェックの強制: `data`がNullかもしれないとコンパイラが認識しているため、不用意なアクセスを未然に防げます。
3. 可読性の向上: 呼び出し側で `result.$1` などと書く必要がなく、直感的に値にアクセスできます。
—
3. 陥りやすい罠:Null許容の伝播
初心者がよくやる間違いは、「とりあえず全部 `?` をつければエラーは消える」と勘違いすることです。
例えば、`({String? name, int? age})` を受け取ったとき、`name` が Null なのか、そもそもデータが存在しないのかの区別がつかなくなります。
解決策:
もし「データがない状態」を明確に表現したいなら、Recordを返すのではなく、Dartの強力な`sealed class`と組み合わせた「Resultパターン」を検討してください。Recordはあくまで「複数の値をまとめて返すための手段」であり、複雑な状態遷移を記述するためのものではない、と理解することが重要です。
—
4. 最後に:Dartを掌握するということ
DartのNull安全は、単なる「エラーを防ぐ機能」ではありません。「コードの意図をコンパイラに正しく伝えるための言語仕様」です。
Record型で多値を返す際、`?` をどこに置くべきか迷ったら、こう自問自答してみてください。
「この値がNullであるとき、呼び出し側は安全に処理を分岐できるか?」
その問いへの答えが、あなたのコードの品質を決めます。
ここをクリアすれば、あなたはもうDartの基本をマスターしたと言っても過言ではありません。次はぜひ、`pattern matching`と組み合わせて、より宣言的で美しいコードを書いてみてください。
Dartの旅はまだ始まったばかりです。一緒に最高峰のコードを書き続けましょう!