go 1.13发布了error
的一些新的特性,那么就来探究学习下。
使用 fmt.Errorf
加上 %w
格式符来生成一个嵌套的 error
,它并没有像 pkg/errors
那样使用一个 Wrap
函数来嵌套 error
,非常简洁。
err1 := errors.New("new error")err2 := fmt.Errorf("err2: [%w]",err1)err3 := fmt.Errorf("err3: [%w]",err2)fmt.Println(err3)
输出
// outputerr3: [err2: [new error]]
err2
就是一个合法的被包装的 error
,同样地,err3
也是一个被包装的 error
,如此可以一直套下去。
拆开一个被包装的 error
func Unwrap(err error) error
将嵌套的 error 解析出来,多层嵌套需要调用 Unwrap 函数多次,才能获取最里层的 error。
源码如下:
func Unwrap(err error) error { // 判断是否实现了 Unwrap 方法 u,ok := err.(interface { Unwrap() error }) // 如果不是,返回 nil if !ok { return nil } // 调用 Unwrap 方法返回被嵌套的 error return u.Unwrap()}
对 err
进行断言,看它是否实现了 Unwrap
方法,如果是,调用它的 Unwrap
方法。否则,返回 nil
。
err1 := errors.New("new error")err2 := fmt.Errorf("err2: [%w]",err2)fmt.Println(errors.Unwrap(err3))fmt.Println(errors.Unwrap(errors.Unwrap(err3)))
输出
// outputerr2: [new error]new error
errors.Is判断被包装的error是是否含有指定错误。
当多层调用返回的错误被一次次地包装起来,我们在调用链上游拿到的错误如何判断是否是底层的某个错误呢?
它递归调用 Unwrap 并判断每一层的 err 是否相等,如果有任何一层 err 和传入的目标错误相等,则返回 true。
源码如下:
func Is(err,target error) bool { if target == nil { return err == target } isComparable := reflectlite.TypeOf(target).Comparable() // 无限循环,比较 err 以及嵌套的 error for { if isComparable && err == target { return true } // 调用 error 的 Is 方法,这里可以自定义实现 if x,ok := err.(interface{ Is(error) bool }); ok && x.Is(target) { return true } // 返回被嵌套的下一层的 error if err = Unwrap(err); err == nil { return false } }}
通过一个无限循环,使用 Unwrap
不断地将 err
里层嵌套的 error
解开,再看被解开的 error
是否实现了 Is
方法,并且调用它的 Is
方法,当两者都返回 true
的时候,整个函数返回 true
。
举个栗子
err1 := errors.New("new error")err2 := fmt.Errorf("err2: [%w]",err2)fmt.Println(errors.Is(err3,err2))fmt.Println(errors.Is(err3,err1))
输出
// outputtruetrue
As这个和上面的 errors.Is
大体上是一样的,区别在于 Is
是严格判断相等,即两个 error
是否相等。而 As
则是判断类型是否相同,并提取第一个符合目标类型的错误,用来统一处理某一类错误。
func As(err error,target interface{}) bool
源码如下:
func As(err error,target interface{}) bool { // target 不能为 nil if target == nil { panic("errors: target cannot be nil") } val := reflectlite.ValueOf(target) typ := val.Type() // target 必须是一个非空指针 if typ.Kind() != reflectlite.Ptr || val.IsNil() { panic("errors: target must be a non-nil pointer") } // 保证 target 是一个接口类型或者实现了 Error 接口 if e := typ.Elem(); e.Kind() != reflectlite.Interface && !e.Implements(errorType) { panic("errors: *target must be interface or implement error") } targettype := typ.Elem() for err != nil { // 使用反射判断是否可被赋值,如果可以就赋值并且返回true if reflectlite.TypeOf(err).Assignableto(targettype) { val.Elem().Set(reflectlite.ValueOf(err)) return true } // 调用 error 自定义的 As 方法,实现自己的类型断言代码 if x,ok := err.(interface{ As(interface{}) bool }); ok && x.As(target) { return true } // 不断地 Unwrap,一层层的获取嵌套的 error err = Unwrap(err) } return false}
举个栗子
type ErrorString struct { s string}func (e *ErrorString) Error() string { return e.s}var targetErr *ErrorStringerr := fmt.Errorf("new error:[%w]",&ErrorString{s:"target err"})fmt.Println(errors.As(err,&targetErr))
输出
// outputtrue
扩展Is As
两个方法已经预留了口子,可以由自定义的 error struct
实现并覆盖调用。
【Go 1.13 errors 基本用法】https://segmentfault.com/a/1190000020398774
【Go语言(golang)新发布的1.13中的Error WrapPing深度分析】https://www.flysnow.org/2019/09/06/go1.13-error-wrapping.html
以上是内存溢出为你收集整理的go1.13errors的用法全部内容,希望文章能够帮你解决go1.13errors的用法所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)