每周技巧 #76:使用 `absl::Status`
本节阅读量:本文翻译自 Abseil 官网的 Tip of the Week #76: Use absl::Status。
原文最初作为 TotW #76 发布于 2014 年 5 月 4 日。
更新于 2020 年 2 月 6 日。
有些人会问什么时候以及如何使用 absl::Status,所以下面给出几个你应该使用 Status 的理由,以及使用时需要记住的事项。
传达意图,并强制调用方处理错误
使用 Status 可以强制调用方处理错误发生的可能性。自 2013 年 6 月起,返回 Status 对象的函数不能被简单忽略。也就是说,下面的代码会产生编译错误:
|
|
而下面这些调用没问题:
|
|
为什么 Status 有一个 IgnoreError() 方法是可以的,而我们又费尽心思让编译器检查 Status 不被忽略?想象你是代码评审者,正在看 CallFoo1() 或 CallFoo2()。后一段代码会让评审者想到:“这个函数本来可能出错,但作者认为忽略它没问题。真的是这样吗?”CallFoo1() 不会触发这种反应。
允许调用方在拥有更多上下文的地方处理错误
当你的代码里并不清楚该如何处理错误时,请使用 Status。也就是说,返回一个 Status,让可能拥有更合适洞察的调用方处理错误。
例如,本地记录日志可能影响性能,比如在编写基础设施代码时。如果你的代码在紧密循环中被调用,即使一次 LOG(INFO) 调用也可能太昂贵。在其他情况下,用户可能并不真正关心某个调用是否成功,并且会觉得日志刷屏很烦。
在许多情况下,记录日志是合适的。但返回 Status 的函数不需要用 LOG 解释为什么失败:它们可以返回失败码和错误字符串,让调用方决定正确的错误处理响应应该是什么。
这不就是重新发明异常吗?
Google 风格指南著名地禁止异常(这是被讨论最多的禁令)。很容易把 absl::Status 看作一种穷人的异常机制,而且开销更大。虽然表面上可能有相似之处,但 absl::Status 的不同点在于它必须被显式处理,而不是像未处理异常那样隐式沿调用栈向上传递。它强制工程师决定如何处理错误,并在可编译代码中显式记录这个决定。最后,使用 absl::Status 返回错误,比抛出并捕获异常快几个数量级。这些特性在写代码时也许显得繁琐,但对所有必须阅读这些代码的人,以及对 Google 整体而言,结果都是净收益。
结论
错误处理是最容易做错的事情之一:这些本来就是边界情况。像 Status 这样的工具,可以在 API 边界、项目、进程和语言之间为错误处理增加一致性,帮助我们尽量减少一大类“我们的错误处理出了问题”的 bug。如果你正在设计一个需要表达失败可能性的接口,并且没有很强的理由不这么做,请使用 Status。