文章目录
- 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
必须是一个指针。