メインコンテンツにスキップ

Unsound null safety

Dartプログラムには、null安全なライブラリとそうでないライブラリが含まれる場合があります。これらの混合バージョンのプログラムは、サウンドではないnull安全性に依存しています。

このように言語バージョンを混在させる機能により、パッケージメンテナーはコードを移行し、レガシーユーザーでも新しいバグ修正やその他の改善を得られるという安心感を持って作業できます。ただし、混合バージョンのプログラムでは、null安全性がもたらすすべての利点を享受できるわけではありません。

このページでは、サウンドnull安全性とサウンドではないnull安全性の違いについて説明し、null安全性への移行時期の判断を支援することを目的としています。概念的な説明の後、段階的な移行の手順、そして混合バージョンのプログラムのテストと実行に関する詳細が続きます。

サウンドとサウンドではないnull安全性

#

Dartは、静的チェックと実行時チェックの組み合わせによってサウンドnull安全性を実現しています。null安全性をオプトインしたDartライブラリはすべて、厳密なコンパイル時エラーを伴う静的チェックを受けます。これは、null安全ではないライブラリを含む混合バージョンのプログラムでも同様です。コードの一部をnull安全性に移行し始めると、すぐにこれらのメリットが得られます。

しかし、混合バージョンのプログラムでは、完全にnull安全なアプリが持つ実行時の安全性保証を得ることはできません。null安全ではないライブラリからnull安全なコードにnullが漏れ出す可能性があります。これは、それを防ぐと移行されていないコードの既存の動作が壊れてしまうためです。

レガシーライブラリとの実行時互換性を維持しつつ、完全にnull安全なプログラムに安全性を提供するために、Dartツールは2つのモードをサポートしています。

  • 混合バージョンのプログラムはサウンドではないnull安全性で実行されます。実行時にnull参照エラーが発生する可能性がありますが、それはnull安全ではないライブラリからnullまたはnull許容型が逃げ出し、null安全なコードに入り込んだ場合に限られます。

  • プログラムが完全に移行され、そのすべてのライブラリがnull安全になった場合、サウンドnull安全性で実行され、安全性によって可能になるすべての保証とコンパイラ最適化が得られます。

可能な限り、サウンドnull安全性を目指してください。Dartツールは、プログラムのエントリポイントライブラリがnull安全性をオプトインしていれば、自動的にプログラムをサウンドモードで実行します。null安全ではないライブラリをインポートすると、ツールは警告を表示し、サウンドではないnull安全性でしか実行できないことを知らせます。

段階的な移行

#

Dartは混合バージョンのプログラムをサポートしているため、プログラムとそのテストを実行できる状態を維持しながら、一度に1つのライブラリ(通常は1つのDartファイル)を移行できます。

まず、他のパッケージのファイル(null安全でないもの)をインポートしないリーフライブラリを移行することをお勧めします。次に、リーフライブラリに直接依存するライブラリを移行します。最後に、パッケージ内で最も多くの依存関係を持つライブラリを移行します。

たとえば、lib/src/util.dartというファイルがあり、他の(null安全な)パッケージやコアライブラリをインポートするが、`import ''`ディレクティブは含まれていないとします。まずutil.dartを移行し、次にutil.dartにのみ依存するファイルを移行することを検討してください。循環インポート(たとえば、AがBをインポートし、BがCをインポートし、CがAをインポートする)がある場合は、それらのライブラリをまとめて移行することを検討してください。

移行ツールの使用

#

移行ツールを使用して、段階的に移行できます。移行ツール。ファイルやディレクトリを除外するには、緑色のチェックボックスをクリックします。次のスクリーンショットでは、binディレクトリ内のすべてのファイルが除外されています。

Screenshot of file viewer in migration tool

除外された各ファイルは、2.9の言語バージョンコメントを除いて変更されません。後でdart migrateを再度実行して、移行を続けることができます。すでに移行されたファイルは無効なチェックボックスで表示されます。一度移行したファイルを移行解除することはできません。

手動での移行

#

パッケージを手動で段階的に移行したい場合は、次の手順に従ってください。

  1. パッケージのpubspec.yamlファイルを編集し、最小SDK制約を少なくとも2.12.0に設定します。

    yaml
    environment:
      sdk: '>=2.12.0 <3.0.0'
  2. パッケージ構成ファイルを再生成します。

    dart pub get

    SDK制約を2.12.0未満にしてdart pub getを実行すると、パッケージ内のすべてのライブラリのデフォルト言語バージョンが2.12に設定され、すべてがnull安全性にオプトインされます。

  3. IDEでパッケージを開きます。
    多くの解析エラーが発生する可能性があります。それは問題ありません。

  4. 現在の移行中に考慮したくないDartファイルの上部に、言語バージョンコメントを追加します。

    dart
    // @dart=2.9

    2.12パッケージ内にあるライブラリに言語バージョン2.9を使用すると、移行されていないコードからの解析エラー(赤い波線)を減らすことができます。ただし、サウンドではないnull安全性は、アナライザーが使用できる情報を減らします。たとえば、アナライザーは、2.9のファイルがnull値を渡す可能性があるにもかかわらず、パラメータ型がnull非許容であると仮定する場合があります。

  5. アナライザーを使用して静的エラーを特定しながら、各Dartファイルのコードを移行します。
    必要に応じて、?!requiredlateを追加して静的エラーを解消します。

混合バージョンのプログラムのテストまたは実行

#

混合バージョンのコードをテストまたは実行するには、サウンドnull安全性を無効にする必要があります。これには2つの方法があります。

  • dartまたはflutterコマンドに--no-sound-null-safetyフラグを使用して、サウンドnull安全性を無効にします。

    dart --no-sound-null-safety run
    flutter run --no-sound-null-safety
  • または、エントリポイント(main()関数が含まれるファイル)の言語バージョンを2.9に設定します。Flutterアプリでは、このファイルはlib/main.dartという名前であることがよくあります。コマンドラインアプリでは、このファイルはbin/.dartという名前であることがよくあります。testの下のファイルもエントリポイントであるため、除外することもできます。例:

    dart
    // @dart=2.9
    import 'src/my_app.dart';
    
    void main() {
      //...
    }

これらのメカニズムのいずれかを使用してテストを除外することは、段階的な移行プロセスのテストに役立ちますが、そうすると、完全なnull安全性を有効にした状態でコードをテストしていることにはなりません。ライブラリの段階的な移行が完了したら、テストをnull安全性にオプトインすることが重要です。