每周技巧 #181:访问 `StatusOr<T>` 的值
本节阅读量:本文翻译自 Abseil 官网的 Tip of the Week #181: Accessing the value of a StatusOr。
原文最初作为 TotW #181 发布于 2020 年 7 月 9 日。
更新于 2020 年 9 月 2 日。
快捷链接:abseil.io/tips/181
StatusOr<Readability>:你不必二选一!
当需要访问 absl::StatusOr<T> 对象内部的值时,我们应该努力让这种访问既安全、清晰,又高效。
注意:本技巧试图强调“光线充足的道路”,为典型用例提供指导。它并不打算穷尽所有情况。如果遇到边界场景,请结合这里的建议和理由自行判断。
建议
访问 StatusOr 所持有的值时,应先调用 ok() 验证值存在,然后通过 operator* 或 operator-> 访问。
|
|
你可以在 if 语句的初始化器中声明 StatusOr,并在条件中检查 ok(),从而限制它的作用域。如果会立即使用一个 StatusOr,通常应该这样限制作用域(见技巧 #165):
|
|
StatusOr 背景
absl::StatusOr<T> 类是一个带值语义的带标签联合,表示以下情况之一且仅有一种:
- 一个类型为
T的对象可用; - 一个
absl::Status错误(!ok()),说明为什么值不存在。
关于 absl::Status 和 absl::StatusOr,可阅读技巧 #76。
安全、清晰和高效
把 StatusOr 对象当作智能指针来处理,有助于代码在保持安全和高效的同时获得清晰性。下面我们会看看你可能见过的其他访问 StatusOr 的方式,以及为什么我们偏好使用间接运算符。
其他值访问器的安全问题
absl::StatusOr<T>::value() 呢?
|
|
这里的行为取决于构建模式,尤其取决于代码是否在启用异常的情况下编译。1 因此,读者不清楚错误状态是否会终止程序。
value() 方法组合了两个动作:先测试有效性,再访问值。因此只有在这两个动作都是你想要的情况下才应使用它(即便如此,也请三思,并注意它的行为取决于构建模式)。如果状态已经已知为 OK,那么理想访问器的语义就是单纯访问值,而这正是 operator* 和 operator-> 所提供的。除了让代码更准确表达意图,这种访问也至少与 value() 先测试有效性再访问值的契约一样高效。
避免同一对象有多个名字
把 absl::StatusOr 对象像智能指针或 optional 值一样处理,也能避免让两个变量指向同一个值的概念尴尬局面。它还避免了由此而来的命名困境和对 auto 的过度使用。
|
|
避免 _or 后缀
使用 StatusOr 变量在检查有效性后直接访问其内在值类型(而不是为同一个值创建多个变量)的另一个好处是,我们可以给 StatusOr 使用最好的名字,而不需要(也不会被诱惑)添加前缀或后缀。
这里可以类比命名指针变量时避免使用 _ptr 后缀。
|
|
如果只有一个变量(这个 StatusOr,并避免为解包后的值创建第二个具名变量),我们就可以去掉后缀,直接按底层值命名变量(就像给指针命名一样)。
|
|
从 absl::StatusOr 的值中移动
我们可能会写代码从 absl::StatusOr<T> 的 T 中移动:
|
|
不过,更好一点的方式是从 StatusOr 本身移动,这向读者(包括人和机器)表明整个 StatusOr 对象在其值被移走后都不应再使用:
|
|
解决方案
像处理智能指针或 optional 一样测试 absl::StatusOr 对象是否有效,并使用 operator* 或 operator-> 访问它,是可读、高效且安全的。
这能帮助你避免上面提到的命名歧义陷阱,而且不需要使用任何宏。
通过 operator* 或 operator-> 访问值的代码(无论是指针、StatusOr、optional 还是其他类型)都必须先验证值存在。这个验证应该靠近访问值的位置,这样读者可以轻松确认该值存在且正确。