Go 中有多种声明错误(Error) 的选项:
errors.New 对于简单静态字符串的错误
fmt.Errorf 用于格式化的错误字符串
实现 Error() 方法的自定义类型
用 "pkg/errors".Wrap 的 Wrapped errors
返回错误时,请考虑以下因素以确定最佳选择:
这是一个不需要额外信息的简单错误吗?如果是这样,errors.New 足够了。
客户需要检测并处理此错误吗?如果是这样,则应使用自定义类型并实现该 Error() 方法。
您是否正在传播下游函数返回的错误?如果是这样,请查看本文后面有关错误包装 section on error wrapping 部分的内容。
否则 fmt.Errorf 就可以了。
如果客户端需要检测错误,并且您已使用创建了一个简单的错误 errors.New,请使用一个错误变量。
Bad | Good |
---|---|
// package foo
func Open() error { return errors.New("could not open") }
// package bar
func use() { if err := foo.Open(); err != nil { if err.Error() == "could not open" { // handle } else { panic("unknown error") } } } |
// package foo
var ErrCouldNotOpen = errors.New("could not open")
func Open() error { return ErrCouldNotOpen }
// package bar
if err := foo.Open(); err != nil { if errors.Is(err, foo.ErrCouldNotOpen) { // handle } else { panic("unknown error") } } |
如果您有可能需要客户端检测的错误,并且想向其中添加更多信息(例如,它不是静态字符串),则应使用自定义类型。
Bad | Good |
---|---|
func open(file string) error { return fmt.Errorf("file %q not found", file) }
func use() { if err := open("testfile.txt"); err != nil { if strings.Contains(err.Error(), "not found") { // handle } else { panic("unknown error") } } } |
type errNotFound struct { file string }
func (e errNotFound) Error() string { return fmt.Sprintf("file %q not found", e.file) }
func open(file string) error { return errNotFound{file: file} }
func use() { if err := open("testfile.txt"); err != nil { if _, ok := err.(errNotFound); ok { // handle } else { panic("unknown error") } } } |
直接导出自定义错误类型时要小心,因为它们已成为程序包公共 API 的一部分。最好公开匹配器功能以检查错误。
// package foo
type errNotFound struct {
file string
}
func (e errNotFound) Error() string {
return fmt.Sprintf("file %q not found", e.file)
}
func IsNotFoundError(err error) bool {
_, ok := err.(errNotFound)
return ok
}
func Open(file string) error {
return errNotFound{file: file}
}
// package bar
if err := foo.Open("foo"); err != nil {
if foo.IsNotFoundError(err) {
// handle
} else {
panic("unknown error")
}
}