【问题标题】:More terse error handling in GoGo 中更简洁的错误处理
【发布时间】:2015-02-09 11:50:54
【问题描述】:

如何处理 Go 中的大量错误?

我查看了我的代码,发现它充满了错误处理程序:

err = result.Scan(&bot.BID, &bot.LANGUAGE, &bot.SOURCE)
if err != nil {
    log.Fatalf("result.Scan: %v", err)
    return
}

fileName, err := copySourceToTemporaryFile(bot)
if err != nil {
    log.Fatalf("copySourceToTemporaryFile: %v", err)
    return
}
...

而且很多行看起来像:

// do something
// handle error
// handle error
// handle error

// do something 2
// handle error
// handle error
// handle error

我可以创建一个打印错误并停止处理的默认处理程序,或者至少将这个“错误处理程序垃圾”移出我的代码逻辑吗?

【问题讨论】:

  • 一开始(后来)看起来很难看,但我碰巧喜欢这种错误捕获。这真的让我想:“在这种情况下我应该怎么做?我应该崩溃(致命),我应该继续使用其他方法/默认值还是应该询问用户”。如果您无论如何都执行log.Fatalf(),您可以定义一个方法CrashIfErr(err error),只需调用log.Fatalf()。顺便说一句:在log.Fatalf() 之后不需要return。

标签: error-handling exception-handling go


【解决方案1】:

这让我想起了 Rob Pike 最近的 Errors are values 以及 Mr. Rob Pike taught me about practice of error handling in Go at GoCon 2014

然而,关键的教训是错误是值,Go 编程语言的全部功能可用于处理它们。

值得强调的是,无论采用何种设计,程序都必须检查错误,无论它们暴露在哪里。这里的讨论不是关于如何避免检查错误,而是关于使用语言优雅地处理错误。

一种技术是定义一个名为errWriter的对象:

type errWriter struct {
    w   io.Writer
    err error
}

write方法调用底层Writer的Write方法并记录第一个错误以备日后参考:

func (ew *errWriter) write(buf []byte) {
    if ew.err != nil {
        return
    }
    _, ew.err = ew.w.Write(buf)
}

一旦发生错误,write 方法将变为空操作,但会保存 error 值。

给定errWriter类型和它的write方法,上面的代码可以重构:

ew := &errWriter{w: fd}
ew.write(p0[a:b])
ew.write(p1[c:d])
ew.write(p2[e:f])
// and so on
if ew.err != nil {
    return ew.err
}

【讨论】:

  • 我以前读过。如果我调用的方法不仅返回错误而且返回不同类型的结构怎么办?
【解决方案2】:

如果error 是“真实的”,如果您不希望在运行时出现意外恐慌,则应该(必须)处理它。

为了补充 VonC 关于errWriter 技术的回答,还有更多可以减少错误处理代码的情况:

当您知道即使函数或方法可能返回error,但不会(例如,您提供的参数来自您知道可以使用的源代码)。在这些情况下,您(或该库的作者)可以提供 helper 函数(或方法),它们不会返回 error,但如果仍然发生,则会引发运行时恐慌。

template 和 regexp 包就是很好的例子:如果你在编译时提供了一个有效的模板或正则表达式,你可以确保它们在运行时总是可以被解析而不会出错。出于这个原因,template 包提供了Must(t *Template, err error) *Template 函数,regexp 包提供了MustCompile(str string) *Regexp 函数:它们不返回errors,因为它们的预期用途是保证输入有效的地方。

例子:

// "text" is a valid template, parsing it will not fail
var t = template.Must(template.New("name").Parse("text"))

// `^[a-z]+\[[0-9]+\]$` is a valid regexp, always compiles
var validID = regexp.MustCompile(`^[a-z]+\[[0-9]+\]$`)

【讨论】:

  • 我喜欢Mustxxx的方式,可以简化错误管理,明确预期。 +1
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-09-03
  • 2014-11-27
  • 1970-01-01
  • 2012-05-25
  • 2012-05-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多