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

这个新的Go错误处理提案能解决问题吗?

时间:2023-04-01 20:10:22 Java

大家好,我是炸鱼。Go语言的一大特点就是它的错误机制,所以基本上我会回顾和学习所有的错误处理建议或讨论,开辟不同的思维视野和解决方案。今天分享的是@CristoGarcía提出的方案《Simple Error Handling for Go 2》,略作修改,和炸鱼一起学习讨论!GomuststillbeGo该提案的核心点是Go必须仍然是Go,这意味着错误处理的改造需要满足以下原则:尽可能少地添加语法。尽可能明确和方便。本文中的“我”指的是提案作者@CristoGarcía,并不是我正在学习的炸鱼。OriginalIdea原提案作者@PeterRk提出了以下想法:funcgetDivisorFromDB(keystring)(uint,error){//...}funcGetDivisor(keystring)(uint,error){exit:=func(errerror)(uint,error){return1,fmt.Errorf("failtogetdivisorwithkey\"%s\":%v",key,err)}divisor:=check(getDivisorFromDB(key),exit)//...returndivisor,nil}用法举例:divisor:=check(getDivisorFromDB(key),exit)等价于现有:divisor,err:=getDivisorFromDB(key)iferr!=nil{returnexit(err)//returnerr}提案作者认为这是正确的方向,我们可以改进它(言外之意:目前的还不够好)。这个最初的想法的问题在于有两个问题:1.包含一个模棱两可的return语句。有时抽象是不必要的,并且会使代码更难阅读。新思路这个新思路需要解决以上两个问题,@CristoGarcía期待能取得更好的效果。通过对语法的简单修改,我们添加了or关键字。可以得到如下例子:divisor,err:=getDivisorFromDB(key)orreturnexit(err)新增的or关键字会检查最后返回的值(必须是error类型)是否和nil不同。如果没有,将执行右侧的功能。我们也可以省略return,代码会继续执行。它将像在常规Go代码中一样被丢弃,因此该函数更具可重用性。下面的例子:funcGetDivisor(keystring)(divisoruint,errerror){divisor,err=getDivisorFromDB(key)orreturnreturn}也就是说orreturn语句后面可以不跟任何东西,它默认情况下将被丢弃。特殊场景:defer这部分只是为了辩论,但我们可以借此机会为defer添加错误检查,看看我们是否可以做一些事情并获得一种新的处理方式。核心思想:如果我们不能将返回的错误保存在一个变量中并在defer中进行或触发,那将是非常有趣的。下面的例子1:deferf.Close()orreturnerrHdl("",fmt.Errorf("couldn'tclosefile"))没有主动显式声明变量,如果返回值是错误类型而不是等于nil,会自动调用右边的函数或者返回并处理。下面的例子2:defererr:=f.Close()orreturnerrHdl("couldn'tclosefile",err)定义了接受错误的变量err,可以直接将参数传入函数errHdl的入参中orreturn的语法被用在了。因此,在添加新的orreturn语法后,将它与原来的错误处理机制进行比较,看看它是如何工作的。新的:funcFoo(pathstring)([]byte,error){errHdlr:=func(reasonstring,errerror)([]byte,error){returnnil,fmt.Errorf("foo%s%w",reason,err)}f,err:=os.Open(path)或returnerrHdlr("无法打开文件",err)deferf.Close()或returnerrHdl("",fmt.Errorf("couldn'tclosefile"))result,err:=io.ReadAll(f)orreturnerrHdlr("couldn'treadfromfile"+path,err)returnresult,nil}旧的:funcFoo(pathstring)([]byte,error){f,err:=os.Open(path)iferr!=nil{returnnil,fmt.Errorf("foo%s%w","无法打开文件",err)}结果,err:=io.ReadAll(f)iferr!=nil{returnnil,fmt.Errorf("foo%s%w","无法从文件中读取"+path,err)}err=f.Close()iferr!=nil{returnnil,fmt.Errorf("foo%s%w","无法关闭thefile"+path,err)}returnresult,nil}这是一个很简单的例子,但是我们已经可以看到这样的好处,就是看代码的程序员甚至可以把注意力放在左边,忽略错误处理。在使用之后gofmt格式化代码,也比较漂亮,下面的例子:f,err:=os.Open(path)orreturnerrHdlr("couldn'topenfile",err)deferf.Close()orreturnerrHdl("",fmt.Errorf("couldn'tclosefile"))result,err:=io.ReadAll(f)或returnerrHdlr("couldn'treadfromfile"+path,err)非常一致。总结在这个新提案中,作者在征求意见阶段,主要提倡or关键字,变量可以传到右函数等各种思路(前段时间我也分享过一个左函数的提案andexpression).作者的目的是为了尽可能的方便,不写以前被吐槽过的iferr!=nil来实现更简单,你觉得这个提议怎么样?欢迎在评论区交流讨论。文章继续更新,可微信搜索【我脑子在炸鱼】阅读。本文已收录在GitHubgithub.com/eddycjy/blog中。学习Go语言可以看Go学习地图和路线。欢迎星星提醒。Go书系列Go语言入门系列:初探Go项目实战Go语言编程之旅:深入使用Go做项目Go语言设计哲学:理解Go的Why和设计思维更像PHP!Go错误处理的新思路?使用左手函数和表达式