一尘不染

避免检查错误是否为零重复?

go

我目前正在学习go,我的一些代码如下所示:

a, err := doA()
if err != nil {
  return nil, err
}
b, err := doB(a)
if err != nil {
  return nil, err
}
c, err := doC(b)
if err != nil {
  return nil, err
}
... and so on ...

这在我看来有点不对劲,因为错误检查占用了大多数行。有没有更好的方法来进行错误处理?我是否可以通过一些重构来避免这种情况?

更新:
感谢您的所有答案。请注意,在我的示例中,doB取决于a,doC取决于b,依此类推。因此,大多数建议的重构在这种情况下不起作用。还有其他建议吗?


阅读 152

收藏
2020-07-02

共1个答案

一尘不染

这是一个常见的投诉,有几个答案。

以下是一些常见的问题:

1-还不错

这是对这些抱怨的非常普遍的反应。实际上,您的代码中有几行额外的代码实际上并不是那么糟糕。这只是廉价的打字,在阅读方面非常容易处理。

2-这实际上是一件好事

这是基于这样的事实,即键入和读取这些额外的行可以很好地提醒您,实际上您的逻辑可能会在此时逃逸,并且您必须撤消放置在其前几行中的所有资源管理。通常将其与异常进行比较,这会以隐式方式破坏逻辑流,迫使开发人员始终牢记隐藏的错误路径。前一段时间,我在这里写了一篇更深入的评论。

3-使用紧急/恢复

在某些特定情况下,您可以通过使用panic已知类型来避免某些工作,然后recover在您的程序包代码问世之前立即使用,将其转换为适当的错误,然后返回该错误。这种技术最常用于展开递归逻辑,例如(un)元帅。

我个人尽量不要滥用过多,因为我与第1点和第2点的关联度更高。

4-稍微重新组织代码

在某些情况下,您可以稍微重新组织逻辑以避免重复。

举一个简单的例子:

err := doA()
if err != nil {
    return err
}
err := doB()
if err != nil {
    return err
}
return nil

也可以组织为:

err := doA()
if err != nil {
    return err
}
return doB()

5-使用命名结果

某些人使用命名结果从return语句中删除err变量。但是,我建议您不要这样做,因为这样做节省的时间很少,降低了代码的清晰度,并且当在纾困返回语句之前定义一个或多个结果时,逻辑上容易产生细微问题。

6-在if条件之前使用语句

正如汤姆·王尔德(Tom
Wilde)在下面的评论中很好地提醒的那样,ifGo中的语句在条件之前接受一个简单的语句。因此,您可以执行以下操作:

if err := doA(); err != nil {
    return err
}

这是一个很好的Go习惯用法,经常使用。

在某些特定情况下,我宁愿避免以这种方式嵌入该语句,只是为了清晰起见而使其独立存在,但这是一个微妙的个人事情。

2020-07-02