go1.13errors的用法

go1.13errors的用法,第1张

概述"go1.13errors的用法" "前言" "基本用法" "fmt.Errorf" "Unwrap&quot go1.13errors的用法前言基本用法fmt.ErrorfUnwraperrors.IsAs扩展参考go1.13errors的用法前言

go 1.13发布了error的一些新的特性,那么就来探究学习下。

基本用法fmt.Errorf

使用 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,如此可以一直套下去。

Unwrap

拆开一个被包装的 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的用法所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1252547.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-07
下一篇 2022-06-07

发表评论

登录后才能评论

评论列表(0条)

保存