当前位置: 首页 > 后端技术 > Java

Go1.20继续对错误库进行小修,..

时间:2023-04-01 21:32:02 Java

大家好,我是炸鱼。Go的错误处理机制一直是无数人提出争论,又被否决的地方。Go1.20近期即将发布,针对errors标准库有新的小修复优化(包裹多个错误)。今天就来了解一下三姑茂露求婚的阉割版,最后不是很成功。回顾Go1.13改进的errors在Go1.13中,errors标准库引入了WrappingError的概念,增加了Is/As/Unwarp三种方法,用于对返回的错误进行二次处理和识别。简单来说,Go的error可以嵌套,提供三种匹配方式。例子:funcmain(){e:=errors.New("Mybrainisfried")w:=fmt.Errorf("Catch:%w",e)fmt.Println(w)fmt.Println(errors.Unwrap(w))}输出结果:$gorunmain.go快速捕获:brainfriedfishbrainfriedfish上面代码中,变量w是一个嵌套错误。最外层是“quickcatch:”,这里调用%w表示嵌套生成WrappingError。所以它最终输出“抓住它:大脑被炸了”。需要注意的是Go并没有提供Warp方法,而是直接扩展了fmt.Errorf方法。由于下面的输出直接调用了errors.Unwarp方法,会“取出”一层嵌套,最后直接输出“脑子炸了”。对WrappingError有了基本的了解之后,我们简单介绍一下三个配套的方法:funcIs(err,targeterror)boolfuncAs(errerror,targetinterface{})boolfuncUnwrap(errerror)errolerrors.Is方法签名:funcIs(err,targeterror)bool方法示例:funcmain(){if_,err:=os.Open("non-existing");err!=nil{iferrors.Is(err,os.ErrNotExist){fmt.Println("文件不存在")}else{fmt.Println(err)}}}errors.Is方法是判断是否传递了err和target是同一类型,如果是则返回true。errors.As方法签名:funcAs(errerror,targetinterface{})bool方法示例:funcmain(){if_,err:=os.Open("non-existing");err!=nil{varpathError*os.PathErroriferrors.As(err,&pathError){fmt.Println("Failedatpath:",pathError.Path)}else{fmt.Println(err)}}}错误。Asmethod是从err错误链中识别出与target相同的类型,如果可以赋值则返回true。errors.Unwarp方法签名:funcUnwrap(errerror)错误方法示例:funcmain(){e:=errors.New("Mybrainisfriedfish")w:=fmt.Errorf("Quicklygrab:%w",e)fmt.Println(w)fmt.Println(errors.Unwrap(w))}该方法的作用是解析出嵌套的错误。如果有多层嵌套,需要多次调用Unwarp方法。问题是在Go1.13之后,我们可以通过fmt.Errorf方法将多个错误存储到错误树中。Errorf方法内部代码如下:funcErrorf(formatstring,a...any)error{...varerrerrorifp.wrappedErr==nil{err=errors.New(s)}else{err=&wrapError{s,p.wrappedErr}}p.free()returnerr}typewrapErrorstruct{msgstringerrerror}简单来说就是在wrapError结构体的基础上实现了Error接口,然后通过层形成错误树。看起来一切都很美好,但是有一个场景没有考虑到……如果有多个错误,或者想把多个错误打包成一个,想自定义怎么办?,我应该怎么办?从逻辑上来说,需要一个一个解包,然后根据需求写自定义逻辑。这很麻烦,而且API也帮不上什么忙。新提案之前也有类似的提案,可惜被否决了。@DamienNeil熟悉围棋团队的流程、规范、风格,再次提出《errors: add support for wrapping multiple errors》,挑战这个备受争议的领域。经过多次让步和讨论,接受了一个错误可以封装多个错误的特性。解决方案是修改Go1.13原有的API,在Go1.20新增errors.Join方法和支持方法转换。Unwrap函数将支持包装多个错误:Unwrap()[]error术语从“错误链”更改为“错误树”。支持的方法errors.Is,errors.As,fmt.Errorf都进行了改造。对应关系如下:errors.Is:如果能匹配到错误则返回true。errors.As:返回第一个匹配错误。fmt.Errorf:将在用户定义的布局中包装多个错误。新的APIJoin函数签名如下:funcJoin(errs...error)error对应示例:funcmain(){err1:=errors.New("err1")err2:=errors.New("err2")err:=errors.Join(err1,err2)fmt.Println(err)iferrors.Is(err,err1){fmt.Println("erriserr1")}iferrors.Is(err,err2){fmt.Println("erriserr2")}}输出结果:err1err2erriserr1erriserr2Join连接的多个错误默认以换行\n分隔拼装。核心是通过新的errors.Join方法将多个错误封装为一个错误。方便您一次提取多个错误。如果需要自定义错误,则需要自己开发。社区对多重错误也有更好的支持,有需要的朋友可以再看看。下面是三个比较有名的库:hashicorp/go-multierrorgo.uber.org/multierrtailscale.com/util/multierr总结Go错误处理已经完成了很多次,虽然这次主要支持包装多个错误,至少可以解决个别场景。比如前两年有同学做formvalidation。在内部系统中,他们希望一次返回所有错误。导致validate[]error只支持返回第一项,没办法简单的一次性全部提取出来。很麻烦。Go1.20将发布本文提到的新提案,从1.13修补到1.20。文章持续更新中。可以微信搜索【脑补炸鱼】阅读。本文已收录在GitHubgithub.com/eddycjy/blog中。学习Go语言可以看Go学习地图和路线。欢迎星星提醒。Go书系列Go语言入门系列:初探Go项目实战Go语言编程之旅:深入使用Go做项目Go语言设计哲学:理解Go的Why与设计思维Go语言进阶之旅:进一步深入-深度Go源码推荐阅读Go1。20号更新了两次时间,终于不用背2006-01-0215:04:05了!打脸兄弟们,Go1.20竞技场来了!Go十年,终于想起统一日志库!