🙌

Flutter Result<T>クラスの実装

に公開

はじめに

Flutter公式ドキュメントで紹介されているcompass_app内にResult<T>というクラスが実装されている。
最初はResult<T>の使い方や実装の意図が分からず混乱したので、備忘録として理解した内容を記録する。

サンプルコード

sealed class Result<T> {
  const Result();

  /// Creates a successful [Result], completed with the specified [value].
  const factory Result.ok(T value) = Ok._;

  /// Creates an error [Result], completed with the specified [error].
  const factory Result.error(Exception error) = Error._;
}

/// Subclass of Result for values
final class Ok<T> extends Result<T> {
  const Ok._(this.value);

  /// Returned value in result
  final T value;

  
  String toString() => 'Result<$T>.ok($value)';
}

/// Subclass of Result for errors
final class Error<T> extends Result<T> {
  const Error._(this.error);

  /// Returned error in result
  final Exception error;

  
  String toString() => 'Result<$T>.error($error)';
}

イメージ


これは処理に成功した際の挙動をイメージしたもの。
処理側がResultクラスにSuccessインスタンスを生成するように要求し、ResultクラスはSuccessインスタンスを生成する。
Errorインスタンスに関しても同様。

疑問

なぜ直接Success/Errorインスタンスを生成しない?

  • Resultのファクトリーメソッドによりインスタンスを生成することで、インスタンスがResultクラスであることが一目でわかる。
  • ._()により直接のインスタンス化を防いでいる。

sealedを使うのはなぜ?

  • sealedクラスのインスタンスはswitch文の条件に使用するとサブクラスに対する処理を網羅しないとエラーになる。→成功/失敗の処理を書き忘れることがなくなる。

Errorコンストラクタには型Tを判断する情報がないのでは?

Future<Result<void>> saveToken(String? token) async {
  // ...
  return Result.error(e);
}
  • return部分だけでは型を判断できないが、メソッドの戻り値として期待するデータ型と整合性がとれるようにコンパイラが型推論を行う。
  • 整合性が取れない場合(ex.return Result<String>.error(e))はエラーになる?

感想

factoryの使い方が少し理解できた。

Discussion