RFC-0060:錯誤處理

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

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

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

摘要

API 通常需要指出要求無法順利完成。錯誤通常會與實用的背景資訊相關聯,讓呼叫端可以採取修正動作。這項提案提供的語法可讓 FIDL 介面說明如何回報錯誤。

提振精神

大多數的程式設計語言都提供錯誤處理結構,例如例外狀況、Futures/Promises、結果類型或錯誤類型。使用不支援此功能的語言編寫的 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。如果 std::expected 真的實現,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 位元,可為錯誤折疊提供許多機會。

這個提案的早期版本允許錯誤為任意資料類型。這可能會鼓勵反模式,例如傳回錯誤訊息字串,並且會限制我們的彈性,因為我們會嘗試將錯誤與碑文對齊,並重新檢查錯誤折疊。我們目前採用較保守的錯誤表示法。

既有技術與參考資料