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