大家好,我是炸鱼。Go语言的一大特点就是它的错误机制,所以基本上我会回顾和学习所有的错误处理建议或讨论,开辟不同的思维视野和解决方案。今天分享的是@CristoGarcía[1]提出的《Simple Error Handling for Go 2[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}注意check函数,第二个参数的exit函数是它在iferr!=nil之后的回调方法,用于err出现时的错误处理。提案作者认为这是正确的方向,我们可以改进它(言外之意:目前的还不够好)。这个最初的想法的问题在于有两个问题: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)返回结果,nil}old: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","无法关闭文件"+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关键字,变量可以传给右函数等各种思路(前段时间我也分享过左函数和表达式的提案)。作者的目的是尽量做到方便,不写以前被吐槽的iferr!=nil,这样更简洁。你觉得这个提议怎么样?欢迎在评论区交流讨论。参考文献[1]CristoGarcía:https://gist.github.com/GGCristo[2]Go2的简单错误处理:https://gist.github.com/GGCristo/27c33308a07c1be216542f1005792c2b
