今天主要记录一下fmt.Errorf() 函数的用法,主要是 %w 和 %v 的区别
fmt.Errors
错误包装
首先开看一段简单的代码搞清楚生成错误的类型
func main() {
baseErr := errors.New("base err")
wbaseErr := fmt.Errorf("werr %w", baseErr)
vbaseErr := fmt.Errorf("verr %v", baseErr)
fmt.Printf("baseErr Type is %T\n", baseErr)
fmt.Println("baseErr value = ", reflect.ValueOf(baseErr))
fmt.Printf("wbaseErr Type is %T\n", wbaseErr)
fmt.Println("wbaseErr value = ", reflect.ValueOf(wbaseErr))
fmt.Printf("vbaseErr Type is %T\n", vbaseErr)
fmt.Println("vbaseErr value = ", reflect.ValueOf(vbaseErr))
}
结果:
baseErr Type is *errors.errorString
baseErr value = base err
wbaseErr Type is *fmt.wrapError
wbaseErr value = werr base err
vbaseErr Type is *errors.errorString
vbaseErr value = verr base err
可见 使用 %w 和 %v 都是对错误添加上下文信息,但是使用%w的时候,返回的是错误的实际类型是*fmt.wrapError
type wrapError struct {
msg string
err error
}
使用 %v的时候,返回的类型和我们平时使用的errors.New() 返回的类型一样
*errors.errorString
error
error 是一个很简单的接口
type error interface {
Error() string
}
下面介绍上文提到的两种比较简单的实现
errors.errorString
就是一个结构体,包括一个错误信息字段s,然后Error方法返回错误信息
type errorString struct {
s string
}
func (e *errorString) Error() string {
return e.s
}
所谓fmt.wrapError
wrap 这个单词的中文意思是 ‘裹’
阅其源码不难发现fmt.wrapError 就是一个上下文信息和一个error的组合 ,
就是将 error 裹起来并附加上下文信息 msg
Error 方法用以返回上下文信息
Unwrap 方法用于返回组合前的error
type wrapError struct {
msg string
err error
}
func (e *wrapError) Error() string {
return e.msg
}
func (e *wrapError) Unwrap() error {
return e.err
}
errors.Is
方法原型
func Is(err, target error) bool
在Is 方法里首先也会使用 == 来比较 error
但是如果使用==比较不成功 接下来 Is 方法会 Unwrap(解包裹) err 参数,并递归调用,继续Is判断
从这里也能理解了使用errors.Is 和使用 == 比较error的区别
fmt.Println(errors.Is(wbaseErr, baseErr)) // true
fmt.Println(errors.Is(vbaseErr, baseErr)) // false
说明
使用 %w生成的wbaseErr 是baseErr 的包装
使用 %v生成的vbaseErr 是将baseErr 转换成了另一种错误类型
error.As
func As(err error, target any) bool
As 方法和Is 方法有相同的返回逻辑,另外,当As 方法返回true的时候,会将err 赋值给 target
func main() {
baseErr := errors.New("base err")
wbaseErr := fmt.Errorf("werr %w", baseErr)
fmt.Println(baseErr == wbaseErr) // false
fmt.Println(reflect.TypeOf(baseErr), "-", reflect.ValueOf(baseErr)) // *errors.errorString - base err
fmt.Println(reflect.TypeOf(wbaseErr), "-", reflect.ValueOf(wbaseErr)) // *fmt.wrapError - werr base err
fmt.Println(errors.As(wbaseErr, &baseErr)) // true
fmt.Println(baseErr == wbaseErr) // true
fmt.Println(reflect.TypeOf(baseErr), "-", reflect.ValueOf(baseErr)) // *fmt.wrapError - werr base err
fmt.Println(reflect.TypeOf(wbaseErr), "-", reflect.ValueOf(wbaseErr)) // *fmt.wrapError - werr base err
}