0%

【文章翻譯】Announcing sound null safety

【文章內容使用 Gemini 1.5 Pro 自動翻譯產生】

宣告可靠的空值安全

作者:Filip Hracek 和 Michael Thomsen

今天,Dart 團隊迎來了一個重要的里程碑,我們推出了空值安全功能的技術預覽版。空值安全可以幫助您避免一類通常難以發現的錯誤,並且額外帶來一系列效能提升。我們現在發佈的是早期技術預覽版,期待您的回饋。

這篇文章描述了 Dart 團隊推出空值安全的計劃。它還解釋了可靠的空值安全是什麼意思,以及它與許多其他語言採用的方法有何不同。

為什麼需要空值安全?

Dart 是一種型別安全語言。這意味著,當您取得某個型別的變數時,編譯器可以保證它是該型別的。但型別安全本身並不能保證變數不是空值。

空值錯誤非常常見。在 GitHub 上搜尋會發現由 Dart 程式碼中的空值引起的數千個問題,以及更多數千個提交試圖修復這些問題。

試著看看您是否可以在以下範例程式碼中發現可空性問題:

1
2
3
4
5
void printLengths(List<File> files) {
for (var file in files) {
print(file.lengthSync());
}
}

如果以空值呼叫此函式,它肯定會失敗,但還有第二種情況需要考慮:

1
2
3
4
5
6
7
void main() {
// 錯誤情況 1:將空值傳遞給 files。
printLengths(null);

// 錯誤情況 2:傳遞檔案列表,其中包含一個空值項目。
printLengths([File('filename1'), File('filename2'), null]);
}

空值安全功能可以解決這個問題:

有了空值安全,您可以更有信心地推斷您的程式碼。不再有煩人的執行階段空值解參考錯誤。相反,您會在編碼時得到靜態錯誤。

可靠的空值安全

Dart 的空值安全是可靠的。這意味著 Dart 可以 100% 確定在上面的範例中,files 列表及其中的元素都不能為空。當 Dart 分析您的程式碼並確定變數不可為空時,該變數始終不可為空:如果您在偵錯器中檢查正在執行的程式碼,您會看到不可空性在執行階段得以保留。相比之下,其他一些實作是不可靠的,並且在許多情況下仍然需要執行執行階段空值檢查。Dart 與 Swift 共享可靠的空值安全,但其他程式語言並不多。

Dart 的空值安全的可靠性還有另一個令人欣喜的含義:它意味著您的程式可以更小、更快。因為 Dart 確實確定 files 永遠不會為空,所以 Dart 可以優化。例如,Dart ahead-of-time (AOT) 編譯器可以產生更小、更快的原生程式碼,因為當它知道變數不為空時,它不需要加入空值檢查。

我們已經看到一些非常有希望的初步結果。例如,在模擬典型 Flutter 架構渲染模式的微基準測試中,我們看到了 19% 的效能提升

設計原則

在開始詳細設計空值安全之前,Dart 團隊定義了以下三個核心原則:

  • 預設不可為空。 除非您明確告訴 Dart 變數可以為空,否則它會將其視為不可為空。我們選擇此作為預設值,因為我們發現非空值是 API 中最常見的選擇。
  • 可逐步採用。 現有的 Dart 程式碼有很多。必須可以逐步遷移到空值安全,一部分一部分地遷移。在同一個專案中應該可以同時存在空值安全和非空值安全的程式碼。我們還將提供工具來幫助您進行遷移。
  • 完全可靠。 如上所述,Dart 的空值安全是可靠的。一旦您將整個專案和您的依賴項遷移到空值安全,您就可以獲得可靠性的全部好處。

使用空值安全宣告變數

核心語法很簡單。以下是一些以不同方式宣告的不可為空變數。記住,不可為空是預設值,因此這些宣告看起來和今天一樣,但它們的含義發生了變化。

1
2
3
4
// 在空值安全的 Dart 中,這些變數都不能為空。
var i = 42;
final b = Foo();
String m = '';

Dart 將確保您永遠不會將空值賦值給上述任何變數。如果您在一千行程式碼之後嘗試執行 i = null,您將收到靜態分析錯誤和紅色波浪線,並且您的程式將拒絕編譯。

如果您希望變數可以為空,您可以使用 ?,如下所示:

1
2
3
4
// 這些都是可為空的變數。
int? j = 1; // 之後可以為空。
final Foo? c = getFoo(); // 函式可能傳回空值。
String? n; // 最初為空。之後的任何時候也可以為空。

上述變數的行為與今天所有變數的行為相同。

您也可以在其他地方使用 ? 語法:

1
2
3
4
5
6
7
8
9
10
11
12
// 在函式參數中。
void boogie(int? count) {
// count 可能為空。
}

// 在函式傳回值中。
Foo? getFoo() {
// 可以傳回空值而不是 Foo。
}

// 也適用於:泛型、型別定義、型別檢查等。
// 以及上述的任何組合。

但是,再次強調,我們的夢想是您幾乎永遠不需要使用 ?。您的絕大多數型別都將是不可為空的。

讓空值安全更容易使用

Dart 團隊正在努力讓空值安全盡可能容易使用。例如,看看這段使用 if 檢查空值的程式碼:

1
2
3
4
5
6
7
8
9
10
void honk(int? loudness) {
if (loudness == null) {
// 未指定 loudness,以最大 loudness 通知開發人員。
_playSound('error.wav', volume: 11);
return;
}

// loudness 不為空,我們將其限制在可接受的級別。
_playSound('honk.wav', volume: loudness.clamp(0, 11));
}

請注意,Dart 如何聰明地意識到,當我們通過該 if 陳述句時,loudness 變數不可能為空。因此,Dart 允許我們直接呼叫 clamp() 方法,而無需額外操作。這種便利性是由一種稱為流程分析的東西實現的:Dart 分析器會像執行程式碼一樣瀏覽您的程式碼,自動找出有關程式碼的額外資訊。

以下是另一個範例,它顯示了一個 Dart 可以確定變數不為空的情況,因為我們總是為其賦值一個非空值:

1
2
3
4
5
6
7
8
9
10
11
12
13
int sign(int x) {
// 結果不可為空。
int result;

if (x >= 0) {
result = 1;
} else {
result = -1;
}

// 到這裡,Dart 知道 result 不可能為空。
return result;
}

如果您移除上述任何賦值(例如,刪除 result = -1; 行),Dart 就無法保證 result 將不為空:您將收到靜態錯誤,並且您的程式碼將無法編譯。

流程分析僅在函式內部有效。如果您有一個全域變數或類別欄位,那麼 Dart 就無法保證何時會為其賦值什麼值。Dart 無法模擬整個應用程式的流程。因此,當您知道變數在第一次讀取之前將不為空,但您無法立即初始化它時,可以使用新的 late 關鍵字。

1
2
3
4
5
6
7
class Goo {
late Viscosity v;

Goo(Material m) {
v = m.computeViscosity();
}
}

請注意,v 不可為空,儘管它最初未初始化。Dart 相信您在為 v 賦值一個非空值之前不會嘗試讀取它,並且您的程式碼可以編譯而不會出錯。

空值安全向下相容

Dart 團隊已經努力了一年多的時間,才讓空值安全進入技術預覽階段。這是自我們推出 Dart 2 以來,對 Dart 語言最大的補充。然而,它並不是一個重大變更。現有程式碼可以呼叫使用空值安全的程式碼,反之亦然。即使空值安全可用後,它也將是一個可選功能,您可以在準備就緒時採用。您現有的程式碼將繼續運行而不會發生變化

我們最近將 Dart 核心程式庫遷移到完全使用空值安全。作為空值安全向下相容的一個範例,我們替換了現有的核心程式庫,而沒有在 Dart 和 Flutter 測試環境中運行的現有測試和測試應用程式中造成任何損壞。我們甚至將新的核心程式庫推送到我們在 Google 內部的許多客戶,直接推送到他們的生產程式碼庫中,也沒有出現任何問題。我們計劃在該功能推出時將我們所有的套件和應用程式遷移到使用空值安全,我們希望您也能這樣做。但您可以按照自己的時間表,逐個套件、逐個應用程式地進行遷移。

空值安全路線圖

我們計劃分三個階段逐步推出空值安全:

  1. 技術預覽。 這是今天發佈的,並且在 Dart 的 dev channel 中可用。有關詳細資訊,請參閱下面的「立即試用」部分。事情仍然可能會發生變化,因此目前請勿在生產程式碼中使用空值安全。不過,請測試一下並 給我們回饋
  2. Beta 版。 空值安全將在 Dart 的 beta channel 中可用,並且不再需要 實驗性標誌。該功能將非常接近預期的最終版本。如果您擁有 pub.dev 套件或外掛,這是您可以開始遷移的時候,但您不應該將其發佈為穩定版本。
  3. 穩定版。 每個人都將擁有空值安全,並且您將被鼓勵將您遷移的套件和外掛發佈為穩定版本。您還應該遷移您的生產應用程式。

如果一切順利,我們計劃在今年年底之前將空值安全作為一個穩定功能發佈。從現在到那時,我們將加入工具來幫助您使您的程式碼空值安全,包括:

  • 一個遷移工具,以支援您自動執行升級現有套件和應用程式的許多步驟
  • pub.dev 上的標籤,以便您可以判斷套件是否支援空值安全
  • pub outdated 命令的擴展,支援尋找支援空值安全的最新版本的依賴項

立即試用

今天試用空值安全的最快方法是透過 nullsafety.dartpad.dev,這是一個啟用了空值安全的 DartPad 版本。打開「使用程式碼片段學習」下拉選單,可以找到一系列學習練習,逐步介紹空值安全的新語法和基礎知識。

您也可以在小型命令行程式中試用空值安全。(我們尚未遷移 Flutter 等較大的框架。)首先下載 dev channel Dart SDK 的副本,然後取得 此範例 Dart CLI 應用程式 的副本(GitHub 儲存庫zip 壓縮檔案)。範例應用程式的 README 檔案 中包含使用空值安全實驗標誌運行應用程式的說明。範例中的其他檔案提供了在 VS Code 和 Android Studio 中啟用偵錯的啟動設定。

我們也有文件,並計劃製作更多:

我們很高興將空值安全引入 Dart。可靠的空值安全是 Dart 的一個獨特功能,可以幫助您編寫更少錯誤的程式碼並獲得更好的效能。我們希望您在技術預覽中試用該功能,並透過我們的問題追蹤器 給我們回饋。編碼愉快!


宣告可靠的空值安全 最初發佈在 Dart 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

http://creativecommons.org/licenses/by/4.0/