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 方法語法,允許介面說明不同的 result 傳回類型和 error 傳回類型。事件一律不會有錯誤類型。 語法如下:

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 方法的程式碼。明確勝過隱含。

測試

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

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

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

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

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

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

既有技術和參考資料