LoginSignup
6
0

Dart 3.0で導入されたRecords型の解説

Last updated at Posted at 2023-06-19

はじめに

Dart 3.0で Records型が導入されました。 Dart 3.0は、Flutter 3.10で採用されているので、Flutter 3.10から Records型が使えます。

本記事では、このRecords型について解説します。

対象読者

  • FlutterやDartの経験がある人で、Dart 3.0の新機能に興味ある人
    • FlutterやDartの基本については説明しません。

Records型を使ってみる

Records型を利用することにより、複雑な型を持つデータを簡単に作成できるようになります。
まずは、簡単な、名前(String)と年齢(int)を返すような関数(userInfo)を作る場合を例にあげて、Records型がどのように役に立つのかを見ていきます。

Listを使う

userInfoの返り値として Listを使って、名前(String)と年齢(int)を返す場合のコードです。

// 名前と年齢を返す関数。
List<Object> userInfo() {
  return ['John', 23];
}

void main() {
  // Listで受け取る
  final List<Object> info = userInfo();
  final String name = info[0] as String; // type safeではない
  final int age = info[1] as int;

  print('name: $name, age: $age');
}

この場合、userInfoのAPIとしての次のようなお約束があります。

  • Listのサイズが 2
  • 0番目の要素の型は String
  • 1番目の要素の型は int

が、たとえ誤ったとしても、コンパイラエラーとならないので、問題の検出は実行時となってしまい、発見が遅れます。

クラスを使う

userInfoの返り値として クラス(UserInfo)を使って、名前(String)と年齢(int)を返す場合のコードです。

// クラスを作る
class UserInfo {
  final String name;
  final int age;
  UserInfo(this.name, this.age);
}

UserInfo userInfo() {
  return UserInfo('John', 23);
}

void main() {
  final UserInfo info = userInfo();
  final String name = info.name;
  final int age = info.age;

  print('name: $name, age: $age');  
}

この場合、さきほどの Listを返すときの問題点はすべて解消されます。
しかしながら、クラス(UserInfo)を作成するという手間がかかります。

Records型を使う

userInfoの返り値として Records型を使う場合のコードです。

(String, int) userInfo() {
  return ('John', 23);
}

void main() {
  final (String, int) info = userInfo();
  final String name = info.$1;
  final int age = info.$2;

  print('name: $name, age: $age');
}

クラスを使う と似ていますが、クラス作成をしていないだけ、短くなっています。

Records型とは

Record 文法

Records型は、無名な、イミュータブルな、アグリゲートな型です。

  • 無名: クラスのように別途作成する必要がありません。その場で作成できます。
  • イミュータブル: 作成したRecords型の値を変更することはできません。
  • アグリゲート: 複数の型をまとめることができます。

Records型は、次のように表現します。

  • フィールドは、コンマで区切る。
  • 全フィールドを()で囲む。
  • 2種類のフィールド
    • ポジションフィールド
    • 名前付きフィールド
var record = ('first', a: 2, b: true, 'last');

この例では、次のように2種類のフィールドが混在しています。

  • 'first' : 1番目のポジションフィールド。 String
  • a: 2: 名前aを持つ、名前付きフィールド。int
  • b: true : 名前bを持つ、名前付きフィールド。bool
  • last: 2番めのポジションフィールド。 String

ポジションフィールドのRecords型

// Records型 recordの宣言。 
(String, int) record;

// recordの初期化
record = ('A string', 123);

// 宣言と初期化をまとめてもよい
// (String, int) records = ('A string', 123);

// $1で 1番目の要素にアクセス
print(record.$1);  // 'A string`
// $2で 2番目の要素にアクセス
print(record.$2);  // 123

名前付きフィールドのRecords型

// Records型 recordの宣言。 
({int a, bool b}) record;

// recordの初期化
record = (a: 123, b: true);

// 宣言と初期化をまとめてもよい
// ({int a, bool b}) records = (a: 123, b: true);

print(record.a);  // 123
print(record.b);  // true

名前付きフィールドのRecords型では、フィールド名が違う場合は、別の型となります。

({int a, int b}) recordAB = (a: 1, b: 2);
({int x, int y}) recordXY = (x: 3, y: 4);

// recordABとrecordXYは別の型とみなされるので、代入するとエラーなる。
// recordAB = recordXY;

ポジションフィールドの場合は、同じ型となります。

// ポジションフィールド場合、a, bは、ドキュメンテーションなので、型に影響しない
(int a, int b) recordAB = (1, 2);
(int x, int y) recordXY = (3, 4);

// 同じ型なので代入できる。
recordAB = recordXY; // OK.

Record フィールド

名前付きフィールドは、名前でアクセスします。
一方、ポジションフィールドは、$<position>でアクセスします。

var record = ('first', a: 2, b: true, 'last');

print(record.$1); // Prints 'first'
print(record.a); // Prints 2
print(record.b); // Prints true
print(record.$2); // Prints 'last'

Record と Pattern

パターンで各要素を取り出すことができる。

ポジションフィールドの場合

// ポジションフィールドのRecordsを返す関数
(String, int) userInfo() {
  return ('John', 23);
}

void main() {
  // 一旦変数(info)で受ける
  final info = userInfo();
  // で、各要素を取り出す。
  print(info.$1);
  print(info.$2);

  // パターンを使って、各要素を直接取り出す
  final (name, age) = userInfo();
  print(name);
  print(age);  
}

名前付きフィールドの場合

// 名前付きフィールドのRecordsを返す関数
({String name, int age}) userInfo() {
  return (name: 'John', age: 23);
}

void main() {
  // 一旦変数(info)で受ける
  final info = userInfo();
  // で、各要素を取り出す。
  print(info.name); 
  print(info.age);

  // パターンを使って、各要素を直接取り出す
  final (:name, :age) = userInfo();
  print(name);
  print(age);

  // (:name, :age)は、 (name: name, age: age)の省略形
  // 別変数で取り出したい場合は、次のように書く
  //final (name :name1, age :age1) = userInfo();
  //print(name1);
  //print(age1);
  
}

あとがき

Records型の概要をざっくりと説明しました。
興味を持った人は、Codelabsをやってみましょう。

Dart 3.0で同じ導入されたPatternについては、最後に軽くふれた程度ですが、Recordsと合わせて使うと非常に強力な武器になるので、Patternについては、別途書こうと思います。

こちらに書きました。

参考

6
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
0