2020/11/10 Dart sound null safety: technical preview 2の訳

Dartは、Flutterで特別な役割を果たし、ホットリロードなどの開発者機能を強化し、Dartの柔軟なコンパイラテクノロジーを介してモバイル、デスクトップ、およびWeb用のマルチプラットフォームアプリを有効にします。私たちは、Dart言語をFlutterアプリ開発者にとって最も生産的なものにするよう努めています。たとえば、FlutterウィジェットツリーをコーディングするためのDart構文を最適化するために、UI-as-code言語構造を追加しました。

6月に、Dartのnull safetyの最初の技術プレビューを提供しました。今日は、私たちがしばらく楽しみにしていたもう1つの主要なマイルストーンです。Flutterフレームワークのサポートを含む、サウンドヌルセーフティの2番目の技術プレビューを発表します。

null safetyは、発見がしいことが多いバグのクラスであるnull exceptionsを回避するのに役立つ主要な新しい生産性機能です。追加のボーナスとして、この機能により、さまざまなパフォーマンスの向上も可能になります。皆様からのフィードバックをお待ちしております。


Why null safety?

Dartはタイプセーフ(型安全)な言語です。これは、あるタイプ(型)の変数を取得すると、コンパイラーがそのタイプ(型)であることを保証できることを意味します。ただし、型安全性自体は、変数がnullでないことを保証するものではありません。

Null errorsは非常に一般的です。GitHubで検索すると、Dartコードの予期しないnullによって引き起こされる何千もの問題が発生し、さらに何千ものコミットがそれらの問題を修正しようとします。以下のFlutterアプリサンプルでNULL値に関する問題を見てみてください。ConfigWeatherServiceはアプリで使用されるバックエンドサービスであると想定してください。

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // ↓サービスからデータを取得します。普通これらの処理は
    // 非同期処理ですが、簡単にするためにこのサンプルでは
    // 同期処理としてコードを書いています。
    final localizedAppName = Config.getAppName();
    final temperatures = WeatherService.getTemperatures();
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text(localizedAppName)),
        body: Padding(
          padding: const EdgeInsets.all(8.0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text('Temperature next 3 days:'),
              for (final t in temperatures) 
                  Text(t.round().toString()),
            ],
          ),
        ),
      ),
    );
  }
}
class Config {
  static String getAppName() { ... }
}
class WeatherService {
  static List<double> getTemperatures() { ...}
}

このアプリはgetAppName()メソッドがnullを返すと、実行時エラーとなります。このケースではAppBarウィジェットのtitleプロパティのTextウィジェットにnullを渡すことになります。

ただし、考慮すべき微妙なケースがさらにあります。getTemperatures()メソッドがnullを返す可能性もあります。

temperaturesにnullがセットされるケース。

このケースではforループでエラーになります。

またはgetTemperatures()メソッドはList<double>を返したが、その要素がnull

を含んでいる可能性もあります。その場合nullに対してround()メソッドを呼び出すことになりますので、エラーが発生します。


nullの安全機能により、入力時にコード検証することで、これらの問題を解決できます。

nullの安全性を使用すると、コードについてより自信を持って推論できます。デプロイされたアプリでの厄介なランタイムnull参照不可エラーはもうありません。代わりに、コーディング時に静的エラーが発生します。


Null safety principles

Dart null安全サポートは、次の3つの主要な設計原則に基づいています。

デフォルトではNon-nullable(null値をとることはできない)です。Dartに対して我々が明示的にnull値をとることがあることを示す場合をのぞき、non-nullableとみなされます。多くのAPIにとってnon-nullの方が遥かに多いので、この設計にしました。

徐々に採用可能。選択したときにnullセーフティに移行し、その後、部分的に段階的に移行することが可能になります。同じプロジェクトにnullセーフコードと非nullセーフコードを含めることができます。また、移行に役立つツールも提供します。

非常に堅実で安定しています。ダーツのヌルセーフティは健全です。これは、型システムを信頼できることを意味します。何かがnullでないと判断された場合、nullになることはありません。これにより、コンパイラの最適化が可能になります。プロジェクト全体と依存関係をnullセーフティに移行すると、バグが少なくなるだけでなく、バ​​イナリが小さくなり、実行が高速化されるという、健全性のメリットを最大限に活用できます。

最初の方に

ただし、型安全性自体は、変数がnullでないことを保証するものではありません。

という説明がありましたが、null safetyを導入すれば、型宣言により(null可型として宣言しなければ、)nullでないことが保証される、という意味。

これらの設計原則をさらに詳しく確認しましょう。


1. Non-nullable by default

コア構文は十分に単純です。さまざまな方法で宣言されたnon-nullable(null許容でない)変数を次に示します。null不可がデフォルトであるため、これらの宣言は、null-safetyが導入されていない現時点での我々のコードと同じように見えますが、意味が変わります。

// null-safe Dartでは下記の変数の値がnullになることはありません。
var widget = Text('Hello');
final status = GetStatus();
String m = '';

2行目はText型のインスタンスが代入された時点でwidget変数の型がText型と型推論される。Text型はnull不可型なので今後変数wedgetにnullをセットすることはできない、ということ。

3行目は、関数GetStatus()の返り値がnull不可型の場合、返り値が代入された時点で変数statusはnull不可型と型推論されるので、その後statusの値がnullになることはない、ということ。statusはfinal(定数)で宣言されているので、その意味でも値を変えることはできないでしょうけども。工事中🏗

4行めはString型で宣言された時点でnull不可型、ということなので、mの値がnullになることはない、ということ。

Dartは、上記の変数のいずれにもnullを割り当てないようにします。数千行後で、

widget = null

↑のコードを記述すると、静的分析エラーと赤い波線が表示され、プログラムはコンパイルを拒否します。


Nullable variables

(ヌル可能変数)

変数をnullable(null可型、null許容)にしたい時は、?を使って以下のように記述します。

// 下記の変数は全てnull可型変数です。
Text? t = Text('Hello');  // ←後に値がnullになる可能性がある。
final Status? s = getStatus();  // ←関数がnullを返す可能性がある。
String? n;  // 初期化されていないのでこの時点で値はnull。後にnull値をとることも可能。

?シンタックスを関数の引数(パラメータ)の型宣言や、返り値の型宣言に使うこともできます。↓

// 関数の引数
void initialize(int? count) {
  // countはnull値の可能性がある。
}
// 関数の返り値
static List<double?>? getTemperatures() {
  // 関数がnullを返す可能性があるし、nullを含むListを返す可能性もある。
}

繰り返しになりますが、理想は、?を使う(null可型を使う)ことはほとんど無い状態です。ほとんどの変数の型はnon-nullable(null不可型)であることが理想です。


Being productive with null safety

(ヌルの安全性で生産性を高める)

null-safetyの利点は安全性だけではありません。生産性を高めるためにこの機能を使用して頂きたいと考えています。

つまり、この機能は使いやすいものでなければなりません。例えば、null値をチェックするためにifを使用する次のコードを見てください。

void honk(int? loudness) {
  if (loudness == null) {
    // loudnessがnullの場合、音量を最大値(11)に固定する↓
    _playSound('error.wav', volume: 11);
    return;
  }
  // Loudnessがnullでない場合、0から11の範囲でクランプする。
  _playSound('honk.wav', volume: loudness.clamp(0, 11));
}

null safetyとは関係ないですが、clampメソッドは

loudnessが指定された範囲内の値ならその値をそのまま返す、

loudnessが最小値(0)より小さい場合、0を返す、

loudnessが最大値(11)より大きい場合、11を返す、

メソッドです。

工事中🏗

この便利さは、フロー分析と呼ばれるものによって可能になります。

Dartアナライザーは、コードを実行しているかのようにコードを処理し、コードに関する追加情報を自動的に把握します。


これは別の例です。これは、変数に常にnull以外の値を割り当てるため、Dartが変数がnullでないことを確認できる場合を示しています。

class StatusLine extends StatelessWidget {
  final Status status;
  StatusLine({this.status: Status.failed});
  @override
  Widget build(BuildContext context) {
    // ↓このローカル変数はnon-nullable(null不可),しかし初期化されていません。
    String statusText;
    if (status == Status.ok) {
      statusText = 'Update succeeded';
    } else {
      statusText = 'Update failed';
    }
    // この段階でDartはstatusTextがnullでないことを認識します。
    // if-else文でどちらのケースでも値をセットしているからです。
    // ですからstatusTextをTextウィジェットに渡すことができます。
    // Textウィジェットにnullを渡すことはできません。エラーが発生します。
    return Text(statusText);
  }

上記サンプルで、例えば11行目の

statusText = ‘Update failed’;

をコメントアウトすると、DartはstatusTextがnon-nullであることを保証できません。ですので、静的エラーが発生しコンパイルできません。DartPadで試してみましょう。

実際11行目をコメントアウトすると、

The non-nullable local variable 'statusText' must be assigned before it can be used

というエラーが表示された。


2. Incrementally adoptable

null safetyはタイピングシステムの根本的な変更であるため、強制的な採用を主張した場合、非常に混乱を招きます。いつがnull-safety導入の適切なタイミングであるかを開発者が判断できるようにしたいので、null-safetyはオプトイン機能です。準備が整う前にnullセーフティを有効にすることを強制されることなく、最新のDartおよびFlutterリリースを使用できます。 。まだ(null-safetyを)有効にしていないアプリやパッケージから、すでにnullセーフティを有効にしているパッケージに依存する(使用する)こともできます。

採用することを選択したら、依存関係グラフの葉を最初に移行して、コードを順番に移行することを強くお勧めします。たとえば、CがAに依存するBに依存している場合、最初にAをnullセーフティに移行し、次にB、次にCに移行します。この順序は、A、B、Cがライブラリ、パッケージ、アプリのいずれであるかに関係なく適用されます。

順序が重要なのはなぜですか?

使用している依存パッケージのマイグレート完了前に、自身のコードのマイグレートをしてしまうと、依存パッケージのマイグレートによってまた自身のコードの変更が必要になる可能性があるからです。

ベータリリースに到達すると、どの依存関係が移行されたかを確認するのに役立つツールが提供されます。パッケージの作成者である場合は、APIが破損するリスクを回避するために、すべての依存関係が移行されるまで待ってから、nullセーフバージョンを公開してください。

依存関係の準備ができたら、移行ツールを使用できます。このツールは、既存のすべてのコードを分析することで機能します。移行ツールはインタラクティブであるため、ツールが推測したnull可能性プロパティを確認できます。ツールの結論のいずれかに同意できない場合は、null可能性のヒントを追加して推論を変更できます。いくつかの移行のヒントを追加すると、移行の品質に大きな影響を与える可能性があります。

3. Fully sound

完全に移行すると、Dartのnull-safetyは健全になります。これは、上記の例では、返り値の変数、list(配列)、およびその要素がnullにならないことをDartが100%確信していることを意味します。

Dartがコードを分析し、変数がNULL不可であると判断した場合、その変数は常にNULL不可です。デバッガーで実行中のコードを調べると、実行時にNULL不可が保持されていることがわかります。

対照的に、他のいくつかの実装は健全ではなく、多くの場合、ランタイムnullチェックを実行する必要があります。DartはSwiftと健全なnull-safetyを共有していますが、他のプログラミング言語はそれほど多くありません。

ダーツのヌルセーフティの健全性には、もう1つの歓迎すべき意味があります。それは、プログラムをより小さく、より速くできることを意味します。Dartは、null許容でない変数が決してnullになることはないと確信しているため、Dartは最適化できます。たとえば、Dart事前(AOT)コンパイラは、変数がnullでないことがわかっている場合にnullのチェックを追加する必要がないため、より小さく高速なネイティブコードを生成できます。

健全なnullの安全性を得るには、プロジェクト全体とすべての依存関係をnullの安全性に移行する必要があることに注意してください。

アプリまたは依存関係の一部が移行されていない場合、部分的なnullの安全性が得られます。これはほとんどのチェックを保持しますが、完全に最適化されておらず、アプリが完全に安全であることを保証しません。


The null safety roadmap

nullセーフティが本番環境で使用できるようになるのはいつですか?現在のタイムラインは次のとおりです。

  1. テクニカルプレビュー2でのフラッター実験:これは現時点で行えます。コアFlutterフレームワークをnullセーフティに正常に移行したため、nullセーフティを試して新しい言語機能を学習し、小さなFlutterサンプルを試すことができます。パッケージの作成者である場合、すでに移行されている小さな依存関係セットがある場合は、試用版の移行を行うこともできます。実験フラグを渡す必要があり、本番環境で使用したり、移行されたパッケージを公開したりしないでください。

2.ベータ版による早期のパッケージ移行:今年の後半に、パフォーマンスチューニングを完了し、機能が意図したとおりに機能し、下位互換性が安定していることを確信できる十分なテストカバレッジを確保します。その時点で、この機能のベータ版を公開する予定であり、実験フラグを渡す必要はありません。パッケージの所有者がパッケージのnullセーフティへの移行を開始することを期待しています。これにより、機能が安定したリリースの準備ができていることを最後に検証できます。

3.安定版での本番環境での使用:次に、ベータ版からのフィードバックに対処し、残りの問題を修正してから、安定版に公開します。具体的なタイムラインを述べるのは難しいですが、来年初めに考えています。機能が安定したら、nullセーフのアプリがストアに公開され、多くのnullセーフパッケージが安定したバージョンでpub.devに公開されるなど、nullセーフティが多く採用されることを期待しています。


Try it now

今日からヌルセーフティの実験を開始できます!すぐに始めるには、nullセーフティ備えたこの特別バージョンのDartPadをチェックしてください。

VS Code、Android Studio、または端末でnullセーフティを試したい場合は、Flutternullセーフティサンプルアプリを確認してください。このアプリには、実行手順と2つのバージョンの小さな天気アプリが含まれています。1つはnullセーフティを使用せず、散発的なnullエラーがいくつかあり、もう1つはnullセーフティを使用してこれらの問題を確実に処理します。

新しいFlutterアプリで実験したい場合は、実行してflutter createから実験手順に従ってnullセーフティを有効にすることができます。Flutterの現在の安定版(stable)とベータ版(beta)はnull-safetyをサポートしていないため、開発チャネル(dev-channel)のFlutter SDK(バージョン1.24.0-3.0.pre以降)が必要になることに注意してください。

機能設計の詳細については、新しい「ヌルの安全性について」のドキュメントを参照してください。短いビデオをご希望の場合は、数か月前のFlutterDayイベントのヌルセーフティビデオご覧ください。

 

 

参考

Medium.com で表示

 

コメントを残す

メールアドレスが公開されることはありません。