RFC-0060:错误处理

RFC-0060:错误处理
状态已接受
区域
  • FIDL
说明

API 通常需要指明请求无法成功完成。通常,与错误相关联的实用上下文信息可让调用方采取纠正措施。本文档提出了一种语法,可让 FIDL 接口描述错误的报告方式。

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

摘要

API 通常需要指明请求无法成功完成。通常,错误会与有用的上下文信息相关联,以便调用方采取纠正措施。这提出了一种语法,允许 FIDL 接口描述错误的报告方式。

设计初衷

大多数编程语言都提供异常、Future/Promise、结果类型或错误类型等错误处理结构。使用不支持这种机制的语言编写的 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++ 可以使用它,但在此期间,我们可以实现足够多的提案来满足 FIDL 的需求。Go 绑定应使用自定义错误类型返回错误。

实施策略

这将通过以下步骤实现:

  • 更新了 fidlc,以支持新语法。
  • 检查它是否定义了正确的编码。
  • 更新了 FIDL 语言文档。
  • 更新了绑定,以使用更符合惯例的错误处理方式。
  • 更新了 FIDL 兼容性测试接口,以测试错误并为所有语言绑定添加支持。
  • 验证语言绑定是否正确互操作。
  • 更新了文档。
  • 鼓励接口作者改进接口以使用错误返回类型。

文档和示例

这是对 FIDL 的重大变更。必须更新语言线格格式文档,以说明新语法及其序列化方式。应更新 FIDL 教程,在其中提供有关如何正确使用错误返回的示例。API 技术评分标准需要更新,以说明如何正确使用此功能。

向后兼容性

大多数现有 FIDL 接口仍将与此更改兼容。 唯一的破坏性更改是 error 变为保留字。

性能

对性能的影响应该非常小。

安全

标准化错误报告语义将简化调用 FIDL 方法的代码。显式优于隐式。

测试

这需要进行 fidlc 测试、兼容性测试,可能还需要进行语言绑定专用测试,以确保绑定符合惯例。

缺点、替代方案和未知情况

这会增加语言的复杂性,但这种复杂性只是描述了接口中已隐式表达的语义。

此提案未涵盖的一个建议是,提供一个标准错误枚举来描述错误类别,并向调用方提供有关后续步骤的提示。此模式适用于 errnozx_status_tHTTP 状态代码。这些示例尝试捕获的详细信息超出了我们认为适当的范围。grpc_status_code 对我们来说是一个更好的模型,可以捕获高级错误。我们正在考虑向标准 FIDL 库添加一个通用错误枚举,以便接口作者可以选择使用该枚举,而不是自行发明。

我们详细讨论了如何将应用错误(例如“未找到记录”)与传输层 FIDL 错误(例如“消息解析失败”)合并。目前,所有传输级 FIDL 错误都会导致通道关闭,因此没有可折叠的错误代码。我们希望能够从此类错误中恢复,但这将在未来的 FTP 中提出。将错误保持为 32 位,可以为错误折叠留出充足的空间。

此提案的早期版本允许错误采用任意数据类型。这可能会导致返回错误消息字符串等反模式,并限制我们在尝试将错误与碑文保持一致并重新检查错误折叠时灵活性。我们目前采用了更为保守的错误表示法。

在先技术和参考文档