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 方法语法,以允许接口描述不同的 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);
};

错误类型必须是 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 位可留出大量机会进行错误折叠。

此方案的早期版本允许任意数据类型的错误。 这可能会鼓励反面模式(例如返回错误消息字符串),并且会限制我们的灵活性,因为我们致力于使错误与对应表保持一致,并重新检查错误折叠情况。 目前,我们采用更保守的错误表示方法。

早期技术和参考资料