1、封装错误error,使其记录错误文件名称、文件路径、行数、操作、错误信息等相关信息。
//封装错误类型,MyError 类型记录了文件,行号,相关的错误信息
type MyError struct {
File string
Line int
Msg string
}
//PathError 除了底层错误外还提供了使用哪个文件,执行哪个操作等相关信息。
type PathError struct {
Path string
Op string
Msg string
}
func (e *MyError) Error() string {
return fmt.Sprintln("%s:%d: %s", e.File, e.Line, e.Msg)
}
func (e *PathError) Error() string {
return fmt.Sprintln("%s:%s: %s", e.Path, e.Op, e.Msg)
}
2、错误处理:直接返回,不去判断具体错误。
func fn() error {
x, err := bar.Foo()
if err != nil {
return err
}
// use x
}
3、断言错误,可以对不同错误类型进行一些简单的相对应的处理。
比如一些网络连接超时的错误,可以断言这种错误类型,然后代码控制重试操作。
4、将错误信息一层一层的往上打,而不是只简单返回错误error
反例:
func AuthenticateRequest(r *Request) error {
err := authenticate(r.User)
if err != nil {
return err
}
return nil
}
错误信息打出去后,仍旧无法定位错误的地方。
正例:
func AuthenticateRequest(r *Request) error {
err := authenticate(r.User)
if err != nil {
return fmt.Errorf("authenticate failed: %v", err)
}
return nil
}
5、给错误添加相关信息,使用包 github.com/pkg/errors,里面有两个主要函数,第一个函数是封装函数 Wrap ,输入一个错误和一个信息,生成一个新的错误返回。第二个函数是 Cause ,输入一个封装过的错误,解包之后得到原始的错误信息。
使用这两个函数,我们现在可以给任何错误添加相关信息,并且在我们需要查看底层错误类型的时候可以解包查看。下面的例子是读取文件内容到内存的函数。
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
}
当发生错误时,输出结果是:
could not read config: open failed: open /Users/dfc/.settings.xml: no such file or directory
6、错误只处理一次,只在需要作出决定时才处理错误,其他时候忽略。