日常在使用golang中的error类型,一般有几种使用方式,分别是使用errors.New
来创建
errors.New("new error")
如果需要添加额外的信息,或者包一层调用某个函数返回的err,可以使用fmt.Errorf
,例如
if err := CallSomeFunc();err != nil{
return fmt.Errorf("err with fmt %v", err)
}
又或者可以自定义Error
类型,查看Error
类型的实现方式
type error interface {
Error() string
}
可以知道只要实现Error()
方法即可,所以自定义类型也可以实现error,例如
type UserError struct {
User string
Err error
}
func (e *UserError) Error() string {
return fmt.Sprintf("user : %s , err: %v", e.User, e.Err)
}
在自定义的时候就可以直接创建自定义类型即可
err := &UserError{User: "walker", Err: errors.New("no exists")}
上述也就是使用error的几个方式,但是在使用过程不乏有痛点,也就是又想保存上下文信息,让信息更全面,又想在调用外层根据不同错误类型来采取不同的处理,一般我们在内部声明了每个固定的错误,例如:
var UserNotExistsError = errors.New("user not exists")
而在外层调用想根据某个返回来判定,即可使用
if e,ok := err.(*UserError);ok && e.Err == UserNotExistsError{
// user component error because of the user not exists
}
有点复杂,如果还包括了其他package调用的错误可能会更难受,但在Go1.13版本中新增了error的一些特性。
简单介绍下,第一个为Unwrap
方法
func (e *UserError) Unwrap() error { return e.Err }
这个就是暴露内部的Err,而配合使用的还有Is
和As
两个方法
if errors.Is(err,UserNotExistsError){
// user not exists
}
官方解释Is
等同于err == UserNotExistsError
而As
相当于类型判定,例如
var e *UserNotExistsError
if errors.As(err, &e) {
}
因为有了Unwrap之后,error可能嵌套了很多层,所以上述两个方法Is
和As
本身就支持嵌套的查询,内部实现可以查看下源码,也是一层层揭开,去做对比判定。
据此Unwrap特性也提供了一个格式化标记%w
,所以可以这样使用
if err != nil {
// Return an error which unwraps to err.
return fmt.Errorf("user %v: %w", user, err)
}
参考: