RFC-0060:錯誤處理

RFC-0060:錯誤處理
狀態已接受
區域
  • FIDL
說明

API 通常需要指出要求無法順利完成。通常錯誤會附上實用的背景資訊,讓呼叫端採取修正措施。這項提案建議採用語法,讓 FIDL 介面說明錯誤回報方式。

作者
提交日期 (年-月-日)2018-09-18
審查日期 (年-月-日)2018-12-06

摘要

API 通常需要指出要求無法順利完成。通常錯誤會附帶實用的背景資訊,讓呼叫端採取修正措施。這項提案的語法可讓 FIDL 介面說明錯誤回報方式。

提振精神

大多數程式設計語言都提供錯誤處理建構函式,例如例外狀況、Future/Promise、結果型別或錯誤型別。如果 API 是以不支援的語言編寫,通常會自行發明 errnozx_status_tGError 等慣例。

FIDL 方法可以傳回多個值,因此方法可能會同時傳回值和錯誤碼,但這種做法不一致,且是臨時性的。通常介面作者會將錯誤碼放在方法結果之前,但有 20% 的情況會顛倒。有時介面會傳回 struct,其中包含狀態碼和結果。狀態會以 boolintenumstringstruct 表示。

開發人員很難瞭解這些 API 的多樣性。 由於缺少明確的語法來區分方法結果與錯誤資訊,因此無法產生慣用的繫結。

設計

我們應擴充 FIDL 方法語法,允許介面說明不同的「結果」傳回型別和「錯誤」傳回型別。事件一律不會有錯誤類型。 語法如下:

interface Example {
  // This method returns no values, failures or completion.
  1: NoReturn();

  // This method returns no values or failures but informs
  // clients of completion.
  2: Completion() -> ();

  // This method returns a single value.
  3: SingleValue() -> (int32 result);

  // This method returns multiple values.
  4: MultipleValue() -> (int32 foo, string bar);

  // This method doesn't return any values but can indicate a
  // failure reason.
  5: CanFail() -> () error int32;

  // This method can succeed with a result or fail with a reason.
  6: WinOrLose() -> (string result) error ExampleError;
};

未宣告錯誤類型的方法會以目前的方式序列化及繫結。回傳引數會編碼為 FIDL struct

宣告錯誤類型的方法會序列化為結果和錯誤傳回類型的 FIDL 聯集。因此,類似下列的方法:

interface Example {
  1: Method() -> (string result, string other_result) error int32;
};

編碼方式如下:

struct ExampleMethodResult {
  string result;
  string other_result;
};
[Result]
union ExampleMethodReturn {
  ExampleMethodResult result;
  int32 err;
};
interface Example {
  1: Method() -> (ExampleMethodReturn return);
};

錯誤類型必須是 int32uint32,或是其中一種型別的 enum

所有現有介面都會與來源和二進位檔相容,但最好是演進為使用新的錯誤語法。

在 FIDL IR 中,代表結果或錯誤的聯集會與其他聯集有所區別,因為前端編譯器會將 [Result] 屬性附加至這些聯集。現有的語言繫結仍可運作,但可以更新,以便支援更多慣用語言功能,代表失敗的方法呼叫。

我們建議 Dart 透過 Future 錯誤傳回失敗。這些錯誤應為 package:fidl 中定義的特殊 Error 型別的子類別,方便區分應用程式層級錯誤與繫結和通訊協定錯誤。

Rust 應使用 std::result::Result。如果 C++ 真的有這種功能,就可以使用 std::expected,但在此之前,我們可以實作足夠的提案內容,滿足 FIDL 的需求。Go 繫結應使用自訂錯誤型別來傳回錯誤。

導入策略

實作方式如下:

  • 更新 fidlc 以支援新語法。
  • 確認是否定義了正確的編碼。
  • 更新 FIDL 語言說明文件。
  • 更新繫結,採用更慣用的錯誤處理方式。
  • 更新 FIDL 相容性測試介面,測試錯誤並新增對所有語言繫結的支援。
  • 確認語言繫結可正確互通。
  • 更新說明文件。
  • 鼓勵介面作者演進介面,使用錯誤回傳型別。

說明文件和範例

這是 FIDL 的重大變更。語言線路格式說明文件必須更新,才能說明新語法和序列化方式。應更新 FIDL 教學課程,提供如何正確使用錯誤回傳的範例。API 技術評量表需要更新,說明這項功能的適當用途。

回溯相容性

大多數現有的 FIDL 介面仍可與這項變更相容。 唯一的重大變更是 error 成為保留字。

效能

對效能的影響應該很小。

安全性

標準化錯誤回報語意可簡化呼叫 FIDL 方法的程式碼。Explicit is better than implicit.

測試

這需要進行 fidlc 測試、相容性測試,以及可能需要語言繫結專屬測試,確保繫結符合慣例。

缺點、替代方案和未知事項

這會增加語言的複雜度,但這種複雜度只是描述介面中已隱含表達的語意。

這項提案未涵蓋的建議之一,是建立標準錯誤列舉,說明錯誤類別,並向呼叫端提供後續步驟的提示。這個模式適用於 errnozx_status_tHTTP 狀態碼。這些範例嘗試擷取的詳細資料超出適當範圍。 grpc_status_code 是更適合我們的模型,可擷取高階錯誤。我們考慮在標準 FIDL 程式庫中新增一般錯誤列舉,介面作者可選擇使用這個列舉,不必自行發明。

我們廣泛討論如何將應用程式錯誤 (例如找不到記錄) 與傳輸層 FIDL 錯誤 (例如訊息剖析失敗) 摺疊在一起。目前所有傳輸層級 FIDL 錯誤都會導致管道關閉,因此沒有要摺疊的錯誤代碼。我們希望能夠從這類錯誤中復原,但這項功能將在未來的 FTP 中提出。將錯誤保留在 32 位元,可為錯誤摺疊留下許多機會。

這個提案的舊版允許任意資料型別的錯誤。這可能會鼓勵反模式 (例如傳回錯誤訊息字串),並限制我們的彈性,因為我們希望將錯誤與墓誌銘對齊,並重新檢查錯誤摺疊。目前我們採用較為保守的錯誤表示法。

既有技術和參考資料