在Golang中有多种声明错误的方式:
errors.New
对于简单的静态字符串的错误fmt.Errorf
用于格式化的错误字符串- 实现
Error()
方法的自定义错误类型 - 用
"pkg/errors".Wrap
的 包装错误
当返回错误时,请考虑以下内容以确定最佳选择:
- 如果是一个简单的错误并且不需要额外的信息,那么
errors.New
就可以满足。 - 如果客户端需要检测并且处理错误,那么你需要自定义一个错误类型,并且实现Error()方法。
- 如果你需要传播下游函数返回的错误,那么你需要对此类错误进行相应的包装。
- 其他情况,
fmt.Errorf即可满足需求
如果客户端需要检测错误,
并且您已经使用errors.New创建了一个简单的错误,请使用var定义有一个错误变量。
Bad
// package foo
//直接返回一个error
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")
}
}
}
Good
// package foo
//在foo包中,定义一个ErrCouldNotOpen 变量,并且此变量是可以导出的(首字母大写)
var ErrCouldNotOpen = errors.New("could not open")
func Open() error {
//直接返回错误变量
return ErrCouldNotOpen
}
// package bar
//在bar包中,
if err := foo.Open(); err != nil {
//根据变量类型进行判断
if err == foo.ErrCouldNotOpen {
// handle
} else {
panic("unknown error")
}
}
如果你有一个错误,客户端可能需要进行检测判断,而且你期望还要添加更多的信息(例如,它不是一个静态的字符串),此种情况,你需要一个自定义的错误类型。
Bad
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")
}
}
}
Good
//自定义一个错误类型
type errNotFound struct {
file string
}
//实现Error()方法
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
//在 foo 包中,自定义错误类型
type errNotFound struct {
file string
}
//实现Error()方法
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
// 在 bar 包中,调用foo包中导出的方法
if err := foo.Open("foo"); err != nil {
if foo.IsNotFoundError(err) {
// handle
} else {
panic("unknown error")
}
}