大家好,试试我是动G刀煎鱼。 Go 的优化错误处理一直是表现最突出的一块地方,许许多多的错误处理同学都提出了各种提案,例如:引入 try-catch、试试用 panic 代替 if err != nil、动G刀引入新的优化关键字等。但这些都被一一驳回了。错误处理 不过社区依然还没放弃,试试周末看到有一个新的动G刀提案《proposal: runtime: add parameters to recover to only return specific types[1]》挺有意思。 今天就由煎鱼带大家一起学习。优化 Go 的错误处理 panic、recover、试试defer 的动G刀基本使用方式。如下代码: package main import "fmt" func mayPanic() { panic("a problem") } func main() { defer func() { if r := recover(); r != nil { fmt.Println("Recovered. Error:\n",优化 r) } }() mayPanic() fmt.Println("After mayPanic()") 输出结果: Recovered. Error: 由于有 Go1 兼容性保障,新提案是在原有的 recover 函数上动小手术。希望可以现在运行时的服务器托管 recover 中加入参数,仅支持返回特定的类型。 函数签名: 案例代码: defer func(){ if err := recover(&MyError{ }, &HelloError{ }); err != nil { switch e := err.(type) { case *MyError: fmt.Println(e) case *HelloError: fmt.Println(e) } } 上面代码中,recover 函数只支持传入 MyError 和 HelloError 的指针类型。再在处理逻辑中根据所传入的错误类型,进行断言分类,实际上去做不同的逻辑处理。 重点就是限定 recover 入参类型。 有些小伙伴可能发现了,对比看看 PHP 的 try-catch 的用法,与前面的提案代码是不是有好几分神似呢? 如下代码: try { ... } catch (Exception $e) { echo $e->getMessage(); } catch (InvalidArgumentException $e) { echo $e->getMessage(); } catch (BarException $ex) { echo $e->getMessage(); } catch (FooException $ex) { echo $e->getMessage(); 本质上这个新的提案是想在不破坏 Go1 兼容性的情况下,在 Go 中实现 try-catch 的方式。 社区在交流时,亿华云计算也发现了用户在现有机制下可以自主实现 Go 的类 try-catch 的模式。 如下代码: func Recover(expect ...interface{ }) interface{ } { if err := recover(); err != nil { if len(expect) == 0 { return err } rv1 := reflect.Indirect(reflect.ValueOf(err)) for _, e := range expect { rv2 := reflect.Indirect(reflect.ValueOf(e)) if rv1.Type() == rv2.Type() { return err } } panic(err) } return nil 这个自制方法的问题是每次 Recover panic 都会将堆栈深度增加 2。因此还是寄希望于 Go 官方能够在运行时支持。 在 Go 标准库的用途对比上,也有产生一定的作用,其涉及到的代码基本为: 标准库中至少有以下地方可以复用: 这个提案本质上是希望通过在 recover 函数中增加入参控制,再配合内部逻辑实现类似 Go 版本的 try-catch 的错误处理机制模型,且实际在 Go 标准库中有复用的空间。 但现阶段该提案也是赞同和反对参半,也有人认为可以用户自己实现,那就没必要加大复杂度,去做这个收益不够大的实现。 你觉得这能补全 Go 错误处理的高防服务器一些机制缺陷吗? [1]proposal: runtime: add parameters to recover to only return specific types: 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/types/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.go: 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