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
。
如果std::expected
成为现实,C++ 可以使用,但与此同时,我们可以实现该方案中足以满足 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 位可留出大量机会进行错误折叠。
此方案的早期版本允许任意数据类型的错误。 这可能会鼓励反面模式(例如返回错误消息字符串),并且会限制我们的灵活性,因为我们致力于使错误与对应表保持一致,并重新检查错误折叠情况。 目前,我们采用更保守的错误表示方法。