更简洁的Go错误处理

huangapple go评论105阅读模式
英文:

More terse error handling in Go

问题

在Go语言中,处理大量错误的一种常见模式是使用错误处理函数。你可以创建一个默认的错误处理函数,用于打印错误信息并停止处理,或者将这些错误处理逻辑从代码中分离出来。

首先,你可以定义一个错误处理函数,例如:

func handleError(err error) {
    if err != nil {
        log.Fatalf("Error: %v", err)
    }
}

然后,在你的代码中,你可以使用这个错误处理函数来处理错误,而不是在每个错误发生的地方都写重复的错误处理逻辑。例如:

err = result.Scan(&bot.BID, &bot.LANGUAGE, &bot.SOURCE)
handleError(err)

fileName, err := copySourceToTemporaryFile(bot)
handleError(err)

// ...

这样,你只需要在每个可能发生错误的地方调用handleError函数即可。

另外,如果你想要在处理错误时继续执行后续的逻辑,而不是停止处理,你可以修改handleError函数的实现,使其只打印错误信息而不终止程序的执行。

希望这些信息对你有帮助!如果你有任何其他问题,请随时提问。

英文:

How do I handle a lot of errors in Go?

I look at my code and find that it is full of error handlers:

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
}
...

And a lot of lines look like:

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

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

Can I create a default handler that prints an error and stops processing, or at least move out this "error-handler-garbage" out of my code logic?

答案1

得分: 5

这让我想起了Rob Pike最近的《错误即值》一文,以及他在2014年GoCon大会上关于Go语言错误处理实践的演讲。

关键的教训是,错误是值,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方法就变成了一个空操作,但错误值会被保存下来。

有了errWriter类型和它的write方法,上面的代码可以重构为:

ew := &errWriter{w: fd}
ew.write(p0[a:b])
ew.write(p1[c:d])
ew.write(p2[e:f])
// 以此类推
if ew.err != nil {
    return ew.err
}
英文:

That reminds me of the recent Errors are values by Rob Pike, as well as Mr. Rob Pike taught me about practice of error handling in Go at GoCon 2014

> The key lesson, however, is that errors are values and the full power of the Go programming language is available for processing them.

> It's worth stressing that whatever the design, it's critical that the program check the errors however they are exposed. The discussion here is not about how to avoid checking errors, it's about using the language to handle errors with grace.

One technique was to define an object called an errWriter:

type errWriter struct {
    w   io.Writer
    err error
}

> The write method calls the Write method of the underlying Writer and records the first error for future reference:

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

> As soon as an error occurs, the write method becomes a no-op but the error value is saved.

> Given the errWriter type and its write method, the code above can be refactored:

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

得分: 1

如果error是"real",如果你不想在运行时出现意外的恐慌,你应该处理它。

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

这些情况是当你知道即使一个函数或方法可能返回一个error,但它实际上不会返回(例如,你从源代码中提供的参数是可行的)。在这些情况下,你(或库的作者)可以提供不返回error但如果错误仍然发生则引发运行时恐慌的_辅助_函数(或方法)。

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

示例:

// "text"是一个有效的模板,解析它不会失败
var t = template.Must(template.New("name").Parse("text"))

// `^[a-z]+\[[0-9]+\]$`是一个有效的正则表达式,总是可以编译
var validID = regexp.MustCompile(`^[a-z]+\[[0-9]+\]$`)
英文:

If the error is "real", you should (have to) handle it if you don't want unexpected panics at runtime.

To supplement VonC's answer about the errWriter technique, there are more cases where you can reduce error handling code:

These are the cases when you know that even though a function or method may return an error, it will not (e.g. you're supplying the parameters from source code which you know will work). In these cases you (or the author of the library) can provide helper functions (or methods) which do not return the error but raise a runtime panic if it still occurs.

Great examples of these are the template and regexp packages: if you provide a valid template or regexp at compile time, you can be sure they can always be parsed without errors at runtime. For this reason the template package provides the Must(t *Template, err error) *Template function and the regexp package provides the MustCompile(str string) *Regexp function: they don't return errors because their intended use is where the input is guaranteed to be valid.

Examples:

// "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]+\]$`)

huangapple
  • 本文由 发表于 2015年2月9日 19:50:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/28409118.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定