可接受的Golang惯用嵌套错误处理?

时间:2016-09-22 13:01:52

标签: go idioms idiomatic

我最近进入Go并看过很多关于如何进行错误处理的讨论。

我看到的模式如下:

err := DoSomething()
if err != nil {
   //handle
}
// continue

经常在管理amqp连接时,我的条件是我只想在错误为nil的情况下继续,因为那时我需要在连接上做一些事情:

c, err := Connect()
if err != nil {
   return nil, err
}
s,err := c.RegisterSomethingOnConnection()
if err != nil {
   return nil, err
}
val, err := s.DoSomething()
return val, err

正如您所看到的,如果从c.RegisterSomethingOnConnection返回的错误为零,我只想运行第Connect()行。

然而,由于提前退货,我不喜欢上述情况。早期的回报让我感到不舒服,因为从长远来看,它会损害可读性,并且在函数退出时会完全模糊。到目前为止,我的解决方案是执行以下操作:

var err error
var val ReturnType

c,err := Connect()
if err == nil {
    s,err := c.RegisterSomethingOnConnection()
    if err == nil {
       val,err = s.DoSomething()
    }
}
return val,err

我喜欢这样做有两个原因。首先,它可以防止返回零。其次,我发现它使代码更易于维护,因为您可以在返回(即日志记录)之前轻松添加功能,并且由于提前返回而没有某些路径错过添加的功能。

我做了什么是可以接受的惯用Go或者我只是需要克服我对早期回报的厌恶并遵循那种模式?

2 个答案:

答案 0 :(得分:3)

其中一个Go Prover是:

不要只检查错误,优雅地处理错误

我建议你阅读Dave Cheney的post

我把重点放在这里:

“没有一种方法可以处理错误。相反,我认为Go的错误处理可以分为三个核心策略”

  • Sentinel错误:

if err == ErrSomething { … }

“使用sentinel值是最不灵活的错误处理策略,因为调用者必须使用相等运算符将结果与预先声明的值进行比较。当您想要提供更多上下文时会出现问题,因为返回不同的错误会破坏平等检查。“

  • 错误类型

if err, ok := err.(SomeType); ok { … }

“错误类型是您创建的实现错误接口的类型。”

  • 不透明错误

x, err := bar.Foo() if err != nil { return err } // use x

“我将此样式称为不透明的错误处理,因为虽然您知道发生了错误,但您无法查看错误内部。作为调用者,您只知道操作的结果是它有效,或者没有。“

....阅读所有帖子。

我认为错误处理的重要方面是不要只检查错误,优雅地处理它们,我希望这可以帮助你。

答案 1 :(得分:1)

GitHub Wiki

  

您应该处理错误,或者不处理错误,但委托给它   更高级别(对呼叫者)。处理错误并将其返回   糟糕的做法好像调用者也这样做,错误可能会得到   处理了几次。

见:
Rob Pike Go. Best practice to handle error from multiple abstract level

Errors are Values

您可以简化它:

if err := datastore.Get(c, key, record); err != nil {

简化重复错误处理:

  

在Go中,错误处理很重要。语言的设计和   约定鼓励您明确检查它们的错误   发生(与其他投掷语言的惯例不同)   例外,有时会抓住它们)。在某些情况下,这使得Go   代码详细,但幸运的是,您可以使用一些技术   尽量减少重复的错误处理。

     

考虑具有检索的HTTP处理程序的App Engine应用程序   来自数据存储区的记录,并使用模板对其进行格式化。

func init() {
    http.HandleFunc("/view", viewRecord)
}

func viewRecord(w http.ResponseWriter, r *http.Request) {
    c := appengine.NewContext(r)
    key := datastore.NewKey(c, "Record", r.FormValue("id"), 0, nil)
    record := new(Record)
    if err := datastore.Get(c, key, record); err != nil {
        http.Error(w, err.Error(), 500)
        return
    }
    if err := viewTemplate.Execute(w, record); err != nil {
        http.Error(w, err.Error(), 500)
    }
}

请参阅:Go Error Handling Techniques