文章目录
- Golang中的error
- error源码
- error创建
- Unwrap()、Is() 和 As()
参考:https://www.flysnow.org/2019/09/06/go1.13-error-wrapping.html
Golang中的error
error源码
type error interface {Error() string
}
error 是一个接口类型,它包含一个 Error() 方法,返回值为 string。任何实现这个接口的类型都可以作为一个错误使用,Error()这个方法提供了对错误的描述。
error创建
- errors.New()
// 源码如下:
package errorsfunc New(text string) error {return &errorString{text}
}// errorString is a trivial implementation of error.
type errorString struct {s string
}func (e *errorString) Error() string {return e.s
}
调用errors.New("xxx")会返回一个errorString的结构体指针,使用fmt.Println(err)时,会自动调用err.Error()方法。
当有些时候我们需要更加具体的信息,即需要具体的“上下文”信息,表明具体的错误值,这就需要用到fmt.Errorf函数。
- fmt.Errorf()
// 源码如下:
func Errorf(format string, a ...interface{}) error {p := newPrinter()p.wrapErrs = truep.doPrintf(format, a)s := string(p.buf)var err errorif p.wrappedErr == nil {err = errors.New(s)} else {err = &wrapError{s, p.wrappedErr}}p.free()return err
}
我们可以先看两个例子:
err1 := fmt.Errorf("这个 fmt.Errorf() 创建的错误,错误编码为:%d", 404)
fmt.Printf("err1 错误类型:%T,错误为:%v\n", err1, err1)err2 := fmt.Errorf("err2: %w", err1) // err3包裹err2错误
fmt.Printf("err2 错误类型:%T,错误为:%v\n", err2, err2)
输出:
err1 错误类型:*errors.errorString,错误为:这个 fmt.Errorf() 创建的错误,错误编码为:404err2 错误类型:*fmt.wrapError,错误为:err2: 这个 fmt.Errorf() 创建的错误,错误编码为:404
结合这两个例子,我们再来看源码:首先,代码调用了newPrinter()方法,返回一个pp结构体指针。pp结构体中有两个字段需要关注wrapErrs和wrappedErr。
wrapErrs:当格式字符串包含%w动词时,将置为true;wrappedErr:记录%w动词的目标;
// newPrinter allocates a new pp struct or grabs a cached one.
func newPrinter() *pp {p := ppFree.Get().(*pp)p.panicking = falsep.erroring = falsep.wrapErrs = falsep.fmt.init(&p.buf)return p
}// pp is used to store a printer's state and is reused with sync.Pool to avoid allocations.
type pp struct {......// wrapErrs is set when the format string may contain a %w verb.wrapErrs bool// wrappedErr records the target of the %w verb.wrappedErr error
}
再看Errorf(),当p.wrappedErr == nil时,errors.New(s)将得到一个errorString的结构体指针(上面提到);
否则,则会返回一个wrapError类型的结构体指针。
func Errorf(format string, a ...interface{}) error {...if p.wrappedErr == nil {err = errors.New(s)} else {err = &wrapError{s, p.wrappedErr}}...
}
wrapError中实现了两个方法:
Error():实现了error接口,表示wrapError是一个error类型;Unwrap():返回原错误值,也就是说拆来一层包装;
type wrapError struct {msg stringerr error
}func (e *wrapError) Error() string {return e.msg
}func (e *wrapError) Unwrap() error {return e.err
}
Unwrap()、Is() 和 As()
Unwrap(): 它的功能就是为了获得被嵌套的error。
func Unwrap(err error) error {u, ok := err.(interface {Unwrap() error})if !ok {return nil}return u.Unwrap()
}
调用一次errors.Unwrap函数只能返回最外面的一层error,如果想获取更里面的,需要调用多次errors.Unwrap函数。最终如果一个error不是warpping error,那么返回的是nil。
Is()
-
如果
err和target是同一个,那么返回true; -
如果
err是一个wrapError,target也包含在这个嵌套error链中的话,那么也返回true;
As()
我们要把error转为另外一个error,一般都是使用type assertion 或者 type switch,其实也就是类型断言。但是现在给你返回的err可能是已经被嵌套了,甚至好几层了,这种方式就不能用了,所以Golang为我们在errors包里提供了As函数。从功能上来看,As所做的就是遍历err嵌套链,从里面找到类型符合的error,然后把这个error赋予target,这样我们就可以使用转换后的target了,这里有值得赋予,所以target必须是一个指针。