当前位置: 首页 > 科技观察

尝试使用Go恢复刀,看看是否可以优化错误处理?

时间:2023-03-15 16:39:41 科技观察

大家好,我是炸鱼。Go的错误处理一直是最突出的领域。很多同学提出了各种各样的建议,比如:引入try-catch,把iferr!=nil换成panic,引入新的关键字等等,但是这些都被一一否决了。然而,社区仍然没有放弃。周末看到一个新的提案《proposal: runtime: add parameters to recover to only return specific types[1]》很有意思。今天炸鱼就带大家一起来学习。语法解释了Go的panic、recover和defer的基本用法。以下代码:packagemainimport"fmt"funcmayPanic(){panic("aproblem")}funcmain(){deferfunc(){ifr:=recover();r!=nil{fmt.Println("Recovered.Error:\n",r)}}()mayPanic()fmt.Println("AftermayPanic()")}输出结果:恢复。Error:一个问题新的提议是基于原来的Go1兼容性保证一些recover函数进行了一些小的操作。希望可以加参数在运行时恢复,只返回特定类型。函数签名:recover(except...interface{})案例代码:deferfunc(){iferr:=recover(&MyError{},&HelloError{});err!=nil{switche:=err.(type){case*MyError:fmt.Println(e)case*HelloError:fmt.Println(e)}}}上面代码中,recover函数只支持指针MyError和HelloError的类型。然后在处理逻辑中,根据传入的错误类型,对断言进行分类,实际上进行不同的逻辑处理。重点是限制recover的入参类型。讨论可能有小伙伴发现了。比较一下PHP中try-catch的用法。是不是有点类似于之前的提案代码?下面的代码:{echo$e->getMessage();}catch(FooException$ex){echo$e->getMessage();}本质上,这个新提议是在不破坏Go1兼容性的情况下在Go中实现try-catch方式。在交流过程中,社区也发现用户可以在现有机制下独立实现Go的try-catch-like模式。以下代码:funcRecover(expect...interface{})interface{}{iferr:=recover();err!=nil{iflen(expect)==0{returnerr}rv1:=reflect.Indirect(reflect.ValueOf(err))for_,e:=rangeexpect{rv2:=reflect.Indirect(reflect.ValueOf(e))ifrv1.Type()==rv2.Type(){returnerr}}panic(err)}returnnil}这种自制方法的问题是每次Recoverpanic都会将堆栈深度增加2。因此,还是希望Go官方能在运行时支持一下。对Go标准库的使用比较也有一定的作用。涉及到的代码基本上是:至少标准库中有以下几个地方可以复用:text/template/exec.go[2]go/parser/interface。go[3]go/types/check.go[4]go/ast/print.go[5]encoding/json/encode.go[6]encoding/gob/error.go[7]将提案总结为希望通过在recover函数中加入输入控制,配合内部逻辑,实现类似Go版本try-catch的错误处理机制模型,在Go标准库中其实也有复用的空间。但现阶段对提案众说纷纭,有人认为可以由用户自己实现,所以没必要增加复杂度,做这个收益不足的实现。你觉得这能弥补Go错误处理的一些缺陷吗?参考资料[1]提案:运行时:添加参数以恢复仅返回特定类型:https://github.com/golang/go/issues/50424[2]text/template/exec.go:https://cs.opensource.google/go/go/+/refs/tags/go1.17.5:src/text/template/exec.go;l=156[3]go/parser/interface.go:https://cs.opensource.google/go/go/+/refs/tags/go1.17.5:src/go/parser/interface.go;l=97[4]go/类型/check.go:https://cs.opensource.google/go/go/+/refs/tags/go1.17.5:src/go/types/check.go;l=235[5]go/ast/print.go:https://cs.opensource.google/go/go/+/refs/tags/go1.17.5:src/go/ast/print.go;l=53[6]encoding/json/encode。去:https://cs.opensource.google/go/go/+/refs/tags/go1.17.5:src/encoding/json/encode.go;l=317[7]encoding/gob/error.go:https://cs.opensource.google/go/go/+/refs/tags/go1.17.5:src/encoding/gob/error.go;l=32