Dart: const コンストラクタの挙動

Dart: const コンストラクタの挙動

September 12, 2022

Flutter で UUID のパッケージを使っているときに疑問に感じたことがありました。

それがこんな場面です。

import 'package:uuid/uuid.dart';

void main() {
  var myId = const Uuid().v4();

  /*
    myId を使って処理をする
  */
}

ここで思ったことが Uuid()const をつけることができるが、const はコンパイル時定数のはずだから、コンパイル後の myId の値は常に同じになってしまうのでは?というものです。

結論から書くと、ここで const をつけても実行時に異なる UUID が生成されます。そのため const をつけて良いものでした。

この結論を確認する過程で const の意味の理解が深まったため、ここに記録しておきます。

パッケージの中身を確認する #

まずパッケージの中身を覗いてみました。

https://github.com/Daegalus/dart-uuid/blob/master/lib/uuid.dart

コードから今回関係するところだけ抜き出すと以下です。

class Uuid {
  const Uuid({this.options});

  String v4({Map<String, dynamic>? options}) {}
}

UUID クラスは const コンストラクタを持っており(先程のコードで const がつけられるため当然ですが)、そして String を返す v4 というメソッドを持っています。

試してみる #

上記を再現するために次のコードを作りました。

main.dart

import 'dart:math';

class MyNumber {
  const MyNumber();

  String generate() {
    return Random().nextDouble().toString();
  }
}

void main() {
  var value1 = const MyNumber().generate();
  var value2 = const MyNumber().generate();

  print('value1: $value1');
  print('value2: $value2');
}

実行すると、異なる ID が生成されていることが確認できます。

dart run main.dart

>> value1: 0.6285126838021444
>> value2: 0.30805691422589065

事前にコンパイルして実行してみる #

結果は変わらないはずですが、事前にコンパイルしてから実行した結果も確認してみます。

コンパイルします。

dart compile exe main.dart

その後実行します。やはり異なる ID が生成されていることが確認できます。

./main.exe

>> value1: 0.20123072181999224
>> value2: 0.8177948189102009

もう少し試してみる #

では、これを実行するとどうなるでしょうか?

import 'dart:math';

class MyNumber {
  const MyNumber();

  String generate() {
    return Random().nextDouble().toString();
  }
}

void main() {
  var myNumber1 = const MyNumber();
  var myNumber2 = const MyNumber();

  var value1 = myNumber1.generate();
  var value2 = myNumber2.generate();

  print('value1: $value1');
  print('value2: $value2');

  print('Same Instance? ${myNumber1 == myNumber2}');
}

結果はこのようになります。生成される ID は異なったものですが MyNumber インスタンスは同じものになっています。

dart run main.dart

>> value1: 0.6635094141767709
>> value2: 0.44454233593829273
>> Same Instance? true

逆に const を外すと…

import 'dart:math';

class MyNumber {
  const MyNumber();

  String generate() {
    return Random().nextDouble().toString();
  }
}

void main() {
  var myNumber1 = MyNumber(); // Before: const MyNumber();
  var myNumber2 = MyNumber(); // Before: const MyNumber();

  var value1 = myNumber1.generate();
  var value2 = myNumber2.generate();

  print('value1: $value1');
  print('value2: $value2');

  print('Same Instance? ${myNumber1 == myNumber2}');
}

インスタンスも異なったものになります。

dart run main.dart

>> value1: 0.5566858718089395
>> value2: 0.4242468509827739
>> Same Instance? false

まとめ #

ここまで見て分かることは const はそのオブジェクト自身を定数化はするが、そのオブジェクトが持っているメソッドが返す結果までは定数化しない、ということです。

自分で手を動かしながら確認すると理解が深まりますね。