【入門編】DartのNull安全と「Record」型の組み合わせ:多値返却の安全な実装 – Dart コア文法・オブジェクト指向・Null安全解析バイブル

やあ。Dartの深淵へようこそ。私はDartのVMやコンパイラ層から言語の進化を見守ってきた者です。

今日は、Dart 3で導入された「Record」と、Dartの心臓部である「Sound Null Safety」を組み合わせた、最もモダンで堅牢な多値返却の作法について話そう。

かつて、複数の値を返すためにわざわざクラスを定義したり、`List`のような型安全性をドブに捨てるようなコードを書いていた時期があったよね。あれはもう過去の話だ。Dart 3以降、私たちは「型」を犠牲にせずに、美しく安全なコードを書けるようになった。

ここをマスターすれば、君のコードは一気にプロフェッショナルな風格を纏うことになる。さあ、深掘りしていこうか。

—

1. なぜ「Record」がNull安全と相性がいいのか

従来の言語では、多値返却をしようとすると「クラスを作る」というオーバーヘッドが発生した。しかし、Recordは「一時的なデータ構造」をコンパイル時に静的に型付けする。

重要なのは、Recordの各要素は、その型定義通りにNull安全の制約を受けるということだ。

例えば、こんな関数を考えてみてほしい。

// ユーザー名と、オプションの年齢を返す関数
// (String, int?) という型が、Recordの構造そのものだ
(String, int?) getUserData() {
// 処理の結果、名前は必須だが年齢は不明かもしれない
return (“Dart Master”, null);
}

この `(String, int?)` という記述。これは単なるデータ構造ではなく、コンパイラに対する「契約」なんだ。`int?`と書いた瞬間、Dartの静的解析器は、呼び出し元に対して「この値はnullかもしれないぞ、チェックしろよ」と警告を発する。この緊密な連携こそが、Dartが「Sound(健全)」である理由だ。

—

2. 現場で使える「安全な多値返却」パターン

現場でよくある「APIから値を取ってきて、成否と結果を返す」という処理を例にしよう。

// 成功した場合はデータを、失敗した場合はエラーメッセージを返す
// (bool, String?, Data?) のような冗長な書き方は避けるのがコツだ
(Data?, String?) fetchData() {
final success = true; // 仮のロジック
if (success) {
return (Data(name: “Flutter”), null);
} else {
return (null, “通信エラーが発生しました”);
}
}

void main() {
// 分解代入(Destructuring)で一気に受け取る
final (data, error) = fetchData();

// ここでNullチェックを強制される。これがSound Null Safetyの強み
if (error != null) {
print(“エラー: $error”);
return;
}

// errorがnullであると確定した後のdataは、
// コンパイラによって「非null」として扱われる(型昇格)
print(“成功: ${data!.name}”);
}

ここがポイント:型昇格の魔法

Dartのコンパイラは優秀だ。`if (error != null)` でガードした瞬間、そのスコープ内では `error` は非nullであると断定できる。さらに、Recordの分解代入を行うことで、コードの可読性が格段に向上するんだ。

—

3. 初学者が陥りやすい「落とし穴」

僕がコードレビューでよく見る「惜しいミス」を挙げておくよ。

① フィールド名なしRecordの混乱

`(String, int)` と書くと、要素へのアクセスは `.1`, `.2` となる。これだと、後でコードを読み返した時に「あれ、1番目は何だったっけ?」となるよね。

改善策:名前付きフィールドを使おう

({String name, int? age}) getUser() {
return (name: “Alice”, age: null);
}

// 呼び出し側も直感的になる
final user = getUser();
print(user.name);

② null非許容型へのnull代入

Recordの型定義で `(String, int)` と指定しているのに、`return (null, 10);` と書くと、コンパイラは容赦なくエラーを吐く。これはバグをコンパイル時に潰せるという最大のメリットだ。もし「nullがあり得る」なら、必ず型定義に `?` を付けること。これを忘れると、コンパイラという強力な味方を敵に回すことになる。

—

4. 伝説のアーキテクトからのアドバイス

DartのNull安全は、ただの「エラーを防ぐための機能」ではない。「設計の意図をコンパイラに伝えるための言語機能」なんだ。

Recordを使って多値を返す時、「これは必ず値があるのか?」「あるいは未定義という状態があり得るのか?」を深く考えて型を定義してほしい。その「迷い」を型定義に落とし込む作業こそが、堅牢なシステムを構築する第一歩になる。

  • 型は厳格に。 `dynamic` や `Object?` で逃げない。
  • 名前付きRecordを活用して、構造に意味を持たせる。
  • 分解代入を活用して、スコープをクリーンに保つ。

これさえ意識すれば、君の書くコードは驚くほど読みやすく、そして壊れにくくなるはずだ。

Dartという言語は、非常に賢い。君が型を通じて「こうあってほしい」と伝えれば、コンパイラはそれを守るために全力でサポートしてくれる。ぜひ、RecordとNull安全を使いこなして、最高のDartライフを送ってくれ。

また何か疑問があれば、いつでも聞いてくれ。君の学びを止めるつもりはないからね。

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