原文链接
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主要讲了几点:
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