| RFC-0060:錯誤處理 | |
|---|---|
| 狀態 | 已接受 |
| 區域 |
|
| 說明 | API 通常需要指出要求無法順利完成。通常錯誤會附帶實用的背景資訊,讓呼叫端採取修正措施。這項提案的語法可讓 FIDL 介面說明如何回報錯誤。 |
| 作者 | |
| 提交日期 (年-月-日) | 2018-09-18 |
| 審查日期 (年-月-日) | 2018-12-06 |
摘要
API 通常需要指出要求無法順利完成。通常錯誤會附帶實用的背景資訊,讓呼叫端採取修正措施。這項提案的語法可讓 FIDL 介面說明錯誤回報方式。
提振精神
大多數程式設計語言都提供錯誤處理建構函式,例如例外狀況、Future/Promise、結果型別或錯誤型別。如果 API 是以不支援的語言編寫,通常會自行發明 errno、zx_status_t 和 GError 等慣例。
FIDL 方法可以傳回多個值,因此方法可能會同時傳回值和錯誤碼,但這種做法不一致,且是臨時性的。通常介面作者會在方法結果之前放置錯誤代碼,但有 20% 的情況是相反。有時介面會傳回 struct,其中包含狀態碼和結果。狀態會以 bool、int、enum、string 和 struct 表示。
開發人員很難瞭解這些 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);
};
錯誤類型必須是 int32、uint32,或是其中一種型別的 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 測試、相容性測試,以及可能需要進行語言繫結專屬測試,確保繫結符合慣例。
缺點、替代方案和未知事項
這會增加語言的複雜度,但這種複雜度只是描述介面中已隱含表達的語意。
這項提案未涵蓋的建議之一,是建立標準錯誤列舉,說明錯誤類別,並向呼叫端提供後續步驟的提示。這個模式適用於 errno、zx_status_t 和 HTTP 狀態碼。這些範例嘗試擷取的詳細資料超出適當範圍。
grpc_status_code 是更適合我們的模型,可擷取高階錯誤。我們考慮在標準 FIDL 程式庫中新增一般錯誤列舉,介面作者可選擇使用這個列舉,不必自行發明。
我們廣泛討論如何將應用程式錯誤 (例如找不到記錄) 與傳輸層 FIDL 錯誤 (例如訊息剖析失敗) 摺疊在一起。目前所有傳輸層級 FIDL 錯誤都會導致管道關閉,因此沒有要摺疊的錯誤代碼。我們希望能夠從這類錯誤中復原,但這項功能將在未來的 FTP 中提出。將錯誤保留在 32 位元,可為錯誤摺疊留下許多機會。
這個提案的舊版允許任意資料型別的錯誤。這可能會鼓勵反模式 (例如傳回錯誤訊息字串),並限制我們的彈性,因為我們希望將錯誤與墓誌銘對齊,並重新檢查錯誤摺疊。目前我們採用較為保守的錯誤表示法。