Don't just check errors, handle them gracefully

原文链接

https://dave.cheney.net/2016/04/27/dont-just-check-errors-handle-them-gracefully

翻译总结下Dave主要分享的几个观点:

避免前哨式的错误处理,因为一些错误处理而引入大量的包,比如下main这种,如果我们处理一个io.EOF,还要引入io包,当然我认为这是蛮常见的处理,但是之前本身并没有注意到这点的坏处,其实也很简单,过度的依赖关系。

if err == ErrSomething { … }

避免错误类型,还是那个问题,首先你的错误类型必须是公开的,如果你的项目需要有特定的错误类型,那么你将无限依赖它,当然这并非不可以,还是过度的依赖会让你的API脆弱。所以建议还是避免错误类型,或者至少避免使它们成为公共API的一部分。

type MyError struct {
        Msg string
        File string
        Line int
}

func (e *MyError) Error() string { 
        return fmt.Sprintf("%s:%d: %s”, e.File, e.Line, e.Msg)
}

return &MyError{"Something happened", “server.go", 42}
err := something()
switch err := err.(type) {
case nil:
        // call succeeded, nothing to do
case *MyError:
        fmt.Println(“error occurred on line:”, err.Line)
default:
// unknown error
}

不透明的错误,这或许是最常见的一种策略,但是也并不是完美的,还是需要一些后续的处理。

import “github.com/quux/bar”

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

断言行为而不是类型

type temporary interface {
    Temporary() bool
}

func IsTemporary(err error) bool {
    te, ok := err.(temporary)
    return ok && te.Temporary()
}

这个例子看着可能蛮复杂,分析一下就是,首先这个错误是实现了temporary接口,如果是这种错误证明它存在一种Temporary的行为,我们可以根据这个isTemporary来进行重试操作。这段代码还是看的很舒服的,我也是第一次知道这种思路。

重点来了,检查完了,还要处理,这里Dave主要讲了几点:

首先是借助github.com/pkg/errors

1.添加上下文

func ReadFile(path string) ([]byte, error) {
    f, err := os.Open(path)
    if err != nil {
        return nil, errors.Wrap(err, "open failed")
    }
    defer f.Close()

    buf, err := ioutil.ReadAll(f)
    if err != nil {
        return nil, errors.Wrap(err, "read failed")
    }

    return buf, nil
}

func ReadConfig() ([]byte, error) {
    home := os.Getenv("HOME")
    config, err := ReadFile(filepath.Join(home, ".settings.xml"))
    return config, errors.Wrap(err, "could not read config")
}


func main() {
    _, err := ReadConfig()
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
}

2.仅处理一次错误

func Write(w io.Writer, buf []byte) error {
        _, err := w.Write(buf)
        if err != nil {
                // annotated error goes to log file
                log.Println("unable to write:", err)
 
                // unannotated error returned to caller
                return err
        }
        return nil
}


func Write(w io.Writer, buf []byte) error {
    _, err := w.Write(buf)
    return errors.Wrap(err, "write failed")
}

前者的Write方法加了一个log,又返回一个err,这样的结果就是不断的返回一直到顶级,看log是一堆err实际上有效的只有一个可能。

end

一个没有高级趣味的人。 email:hushui502@gmail.com
原文地址:https://www.cnblogs.com/CherryTab/p/12797017.html