Dart 3 でやってくるRecord型の紹介

どうも、SupabaseでDevRelをしているタイラーです！

12月9日にFlutter公式Twitterアカウントより2023年の中頃までにDart 3がリリースされ、Flutterもそれに移行することが発表されましたね。

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Flutter&#39;s secret sauce is Dart. By mid 2023 we&#39;ll take a big step together with Dart 3.<br><br>Learn how we are preparing for the next major release, where Dart only supports sound null safety, and get a preview of the new features.<br><br>Read the blog by <a href="https://twitter.com/MiSvTh?ref_src=twsrc%5Etfw">@MiSvTh</a> → <a href="https://t.co/VArkDwIVHg">https://t.co/VArkDwIVHg</a> <a href="https://t.co/iozxTC4z7C">pic.twitter.com/iozxTC4z7C</a></p>&mdash; Flutter (@FlutterDev) <a href="https://twitter.com/FlutterDev/status/1600988602686636032?ref_src=twsrc%5Etfw">December 8, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

今回はそんなDart 3で新しくレコード型という型が追加されるのですが、これが便利なので紹介させてください。

*ここで書いていることはまだ開発中の機能もあるのでもしかしたら変更になることもあるかもしれません。あくまで「こんな雰囲気の機能がやってくるんだ」くらいの感じで読んでいただけたら幸いです。


## Record型の基礎

Dartでは今まで複雑な型を持つデータを定義する際にいちいちクラスを定義しなくてはならなかったのですが、このレコード（Record）型の登場で毎回クラスを定義する必要がなくなり、TypeScriptなど他の言語に近い感覚で開発をすることができるようになりました。また、他の言語で目にするTuple的な使い方もできるようになっています。

レコードは`()`を使って書き、また引数の定義の仕方と同じで`{}`でプロパティを囲うことでそのパラメーターをnamed parameterにすることができます。

```dart
// Positional argumentのみを使った例
(int myNumber, String myName) data = (1, 'Jon');
final myNumber = data.$0; // Positional argumentは`$[index]`の書き方でアクセスする
final myName = data.$1;

// Positional argumentとnamed parameterを混ぜた例
(int myNumber, {String myName}) data = (1, myName: 'Jon');
final myNumber = data.$0;
final myName = data.myName; // Named parameterは普通にそのパラメーター名でアクセスできる
```
## 実例

これを使うとコードをスッキリ書くことができるシチュエーションがあったりします。
例えば、パジネーション機能を実装するときにロードしたデータ、現在のページ、総ページ数の三つの値をAPIから引っ張ってきて返すような関数があったとしましょう。Dart 2ではこんな感じで書けると思います。

```dart
class PaginatedData {
  final List<Item> items;
  final int currentPage;
  final int totalPages;

  PaginatedData({
    required this.items,
    required this.currentPage,
    required this.totalPages,
  });
}

Future<PaginatedData> getPaginatedData(int currentPage) async {
  // APIリクエストを投げるなどしてデータを引っ張ってくる

  return PaginatedData(
    items: items,
    currentPage: currentPage,
    totalPages: totalPages,
  );
}
```

ここの例で言う`PaginatedData`は特にクラスとして意味はなくただ単に３つの値をいっぺんに返すためだけに作られたクラスです。Dartで開発をしているとこのように複数の値を返すためだけのクラスが増えがちです。しかし、新しくできたレコード型を使うとこんな風にスッキリ書くことができます。

```dart
Future<({ List<Item> items, int currentPage, int totalPages })>
 getPaginatedData(int currentPage) async {
  // APIリクエストを投げるなどしてデータを引っ張ってくる

  return (
    items: items,
    currentPage: currentPage,
    totalPages: totalPages,
  );
}
```

余計なクラスがなくなりだいぶコードがスッキリした感じがありますね。値は一度関数の戻り値を変数に受け取り、それのプロパティにアクセスすることできます。

```dart
final data = await getPaginatedData(currentPage);
final items = data.items;
final currentPage = data.currentPage;
final totalPages = data.totalPages;
```

これも悪くはないのですが、実はDestructuringという機能を使い一発でこれら三つの変数を取り出すこともできます。

```dart
final (:items, :currentPage, :totalPages) = await getPaginatedData(currentPage);
```

この一行だけで`items`, `currentPage`, `totalPages`という変数が作られ、それぞれ値が入ってくる形になります。

全体的に使い勝手はTypeScriptのオブジェクトとだいぶ類似していますね。昨今はBackendの構築でTypescriptを触っている人も多いかと思うのでだいぶこのアップデートは嬉しいのではないでしょうか！

レコード型の詳しい仕様は[こちら](https://github.com/dart-lang/language/blob/master/accepted/future-releases/records/records-feature-specification.md)に書いてあるので興味がある方は見てみてください。

また、[DartのDevチャンネル](https://dart.dev/get-dart/archive#dev-channel)ではすでにDart 3を試すことができますので気になる方は触ってみてください！