Go中有效的错误处理

原文链接:https://morsmachine.dk/error-handling
翻译:devabel

介绍

Go得到很多批评的事情之一就是如何处理错误。尽管必须明确检查每个错误似乎令人望而生畏,但您可以采取一些措施来防范错误的错误处理。

以缩进流处理错误

在编写Go代码时,建议:

f, err := os.Open(path)
if err != nil {
    // handle error
}
// do stuff

不建议:

f, err := os.Open(path)
if err == nil {
    // do stuff
}
// handle error

这样,无错误的情况下,业务逻辑将直线顺序执行。

定义你的错误

当知道有错误发生的时候必须首先知道怎么传递错误。如果您的包可能以某种方式导致错误,那么您的用户可能有兴趣知道怎么造成了错误。要做到这一点,你只需要实现错误接口,就是这样简单的事情:

type Error string

func (e Error) Error() string { return string(e) }

使用您得包的用户现在可以通过执行类型断言来判断您的包是否导致错误

result, err := yourpackage.Foo()
if ype, ok := err.(yourpackage.Error); ok {
    // use ype to handle error
}

这也可以用来向用户公开结构化错误信息。

type ParseError struct {
    File  *File
    Error string
}

func (oe *OpenError) Error() string {
    // format error string here
}

func ParseFiles(files []*File) error {
    for _, f := range files {
        err := f.parse()
        if err != nil {
            return &OpenError{
                File:  f,
                Error: err.Error(),
            }
        }
    }
}

这样,您的用户现在可以分辨哪个文件无法解析。

尽管如此,你应该小心包装错误。当您包装错误时,信息可能会丢失。

var c net.Conn
f, err := DownloadFile(c, path)
switch e := err.(type) {
default:
    // this will get executed if err == nil
case net.Error:
    // close connection, not valid anymore
    c.Close()
    return e
case error:
    // if err is non-nil
    return err
}
// do other things.

如果你包装net.Error,这段代码将不会看到它是网络失败并重新使用无效连接。

一个好的经验法则是,如果你的软件包使用外部接口,不要将通过调用产生的错误包装起来。您的用户可能比您的更关心他们的错误。

状态错误

有时候您可能想要坚持发生错误,因为您可以延迟报告,或者您知道很快会再次报告。

第一种情况的一个好例子是bufio包。当bufio.Reader遇到错误时,它会一直等到错误发生,直到缓冲区被清空。只有这样它才会报告它。

第二种情况的一个好例子是go / loader。当使用导致错误的参数进行调用时,它会保留错误,因为它很可能会再次用相同的参数调用。

使用函数来避免重复
如果你有一个重复的错误处理,你可以使用一个函数。

func handleError(c net.Conn, err error) {
    // repeated error handling
}

func DoStuff(c net.Conn) error {
    f, err := downloadFile(c, path)
    if err != nil {
        handleError(c, err)
        return err
    }
    
    f, err := doOtherThing(c)
    if err != nil {
        handleError(c, err)
        return err
    }
}

另一种写作方式是

func handleError(c net.Conn, err error) {
    if err == nil {
        return
    }
    // repeated error handling
}

func DoStuff(c net.Conn) error {
    defer func() { handleError(c, err) }()
    f, err := downloadFile(c, path)
    if err != nil {
        return err
    }
    
    f, err := doOtherThing(c)
    if err != nil {
        return err
    }
}

就这样。
这就是它的全部。

你可能感兴趣的:(Go中有效的错误处理)