错误处理项目组的工作进展

2020 年 11 月 23 日 · Sean Chen 代表库团队

Rust 社区认真对待错误处理。目前已经形成了强调有用的错误处理和报告的强大文化,许多库都提供了自己的实现(参见 Jane Lusby 对 Rust 错误处理/报告库的全面调查)。

但仍有改进的空间。该小组的主要重点是继续进行在小组成立之前已在进行的错误处理相关工作。为此,我们正在系统地解决与错误处理相关的问题,并消除阻碍停滞不前的 RFC 的障碍。

在我们的前几次会议中,我们设定了一些短期和长期目标。这些目标分为三个主题:使 Error trait 更具通用性、改进错误处理的人机工程学,以及编写额外的学习资源。

一个标准化的 Error Trait

Error trait 自 1.0 版本以来就已存在,并公开了两个方法:Error::descriptionError::cause。按照其最初的设计,它由于多种原因1而过于严格。Failure crate 通过导出 Fail trait 解决了 Error trait 的许多缺点,这也为改进 Error trait 提供了许多参考。

关于这一点,增强 std::error::Error trait,使其能在整个 Rust 社区中被采纳为唯一的 Error trait,自 2018 年 8 月 RFC 2504 合并以来一直在进行中。

这个过程还包括稳定许多 Error trait API 和 crate,截至本文撰写时,它们仅在 nightly 版本中可用。其中包括 backtracechain 方法,这两个方法对于处理错误类型都非常有用。如果您有兴趣关注或贡献这项工作,请查看此 issue

另一项相关举措是将 Error trait 迁移到 core 中,使其在不同用例(例如 FFI 或嵌入式环境)中更易于访问。

更多访问错误上下文的方式

凭借 Result 类型和 ? 运算符,Rust 的语言语义已经提供了相当不错的人机工程学错误处理体验。错误处理小组已经确定了一些额外的功能,以进一步改善错误处理的用户体验。

添加迭代 Backtrace 类型的功能

截至本文撰写时,backtrace 类型仅实现了 DisplayDebug trait。这意味着处理 backtrace 类型的唯一方法是将其打印出来,这并不理想。一个提供迭代堆栈帧能力的迭代器 API 将使用户能够控制其回溯信息的格式,这是向 color-backtrace 等 crate 添加 std::backtrace::Backtrace 支持的必要步骤。

在研究如何解决此问题的策略时,我们发现 backtrace crate 已经有一个 frames 方法,该方法非常适合实现 Iterator API。在 std 中公开一个相同的方法应该是一个相对简单的过程。

已为此打开了一个PR,欢迎任何有兴趣的人查看。

泛型成员访问

目前,当我们想要获取与错误相关的额外上下文时,需要调用特定的方法来获取该上下文。例如,要查看错误的回溯信息,我们会调用 backtrace 方法:let backtrace = some_error.backtrace();。这种方法的缺点是无法支持在 std 之外定义的类型。即使对于 std 中存在的类型,也需要定义一个方法来访问每种类型,这使得事情变得麻烦且难以维护。

顾名思义,泛型成员访问(当实现后)是一种与类型无关的方式,用于从 Error trait 对象访问不同的上下文片段。对我来说,一个恰当的类比是你将字符串解析为数字时,就像这样:

let ten = "10".parse::<i32>();

或者当你收集迭代器产生的内容时:

use std::collections::HashSet;

let a_to_z_set = ('a'..='z').collect::<HashSet<_>>();

类似地,你可以通过指定其类型 ID 来从错误中访问某些上下文片段:

let span_trace = some_error.context::<&SpanTrace>();

这可以用于获取与错误相关的其他上下文片段,例如其回溯信息、错误的来源、状态码、备用格式表示(例如 &dyn Serialize)。

此功能将启用我们计划后续添加的其他功能,例如公开一种报告程序中所有错误起源位置的方式,以及除了 DisplayDebug 之外,公开一种更一致的错误报告格式。

Jane 在推进这些想法方面付出了大量努力。你可以查看相关的RFC

编写一本关于 Rust 错误处理最佳实践的书

最后但同样重要的是,小组对编写《Rust 错误处理之书》抱有很大兴趣。这本书的目的是根据不同的用例对各种错误处理最佳实践进行整理和传达。这可能包括 FFI 用例,或关于从程序返回错误码的最佳实践。

这是一项正在进行的工作,未来几周和几个月将取得很大进展!

总结

我们对有机会继续迭代和改进 Rust 的错误处理人机工程学和文化感到兴奋!如果您有兴趣提供帮助和/或加入讨论,请前来并在我们的Zulip stream 中自我介绍。您也可以通过我们的GitHub repo 跟踪我们的进展。

最后,在我们的下一次更新中,我们将介绍有关一种普适性错误报告格式的最新消息,敬请期待!

注脚

1Error::description 方法只支持字符串切片,这意味着创建包含额外上下文的动态错误消息并不简单。此方法已被弃用,转而使用 DisplayError::cause 方法,现在称为 Error::source,不强制要求错误具有 'static 生命周期,这意味着无法向下转型错误来源,使得使用动态错误处理器处理错误变得更加困难。