| 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 方法语法,以允许接口描述不同的结果返回类型和错误返回类型。事件永远不会有错误类型。 语法如下所示:
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 位,为误差折叠留下了很大的空间。
此提案的早期版本允许错误为任意数据类型。这可能会鼓励返回错误消息字符串等反模式,并且会限制我们在尝试将错误与墓志铭对齐并重新检查错误折叠时的灵活性。目前,我们采用了一种更保守的错误表示方法。