大家好,我是煎鱼。
在近日新的 try 提案《proposal: Go 2: error handling: try statement with handler》在社区引发了热议。
明天煎鱼和大家一起关上来看看,这能把 Go 错误处理机制给掀开重整不。
背景
来自 PingCAP 的提案作者 @Greg Weber 会干这事基于两个因素,一个是在《Go Developer Survey 2022 Q2 Results》中明确提到。
随着 Go1.18 泛型的公布,原先矛盾最深的泛型曾经失去一个初步的解决方案。在社区调研上,开发者在应用 Go 时面临的最大挑战曾经转移到了错误处理上,须要投入精力去 “解决” 它。
另外一个因素就是众所皆知的,Go 错误处理代码比拟繁琐,常被工程师们戏称一个 Go 工程里有 30% 都 if err = nil。
如下代码:
_, err := f()if err != nil { ...}_, err = r()if err != nil { ...}_, err = w()if err != nil { ...}
心愿让其更优雅。也有许多小伙伴认同这个设计,的确是简略、直观的解决,在社区造成了角力。
try-handler 提案
本次提案中所提到的解决方案,是减少一个新语句 try ,以此达到简洁的错误处理的作用,让 if err != nil 的解决丝滑起来。
如下代码:
try err, handler
编译器翻译后生成的代码:
if err != nil { return handler(err)}
在函数中能够如下:
func(args...) (rtype1, rtypes..., rtypeN, error) { try err, handler ...}
翻译后生成的代码:
func(args...) (rtype1, rtypes..., rtypeN, error) { if err != nil { return Zero(rtype1), Zeros(rtypes...)..., Zero(rtypeN), handler(err) } ...}
也能够只针对 if err != nil 解决。如下代码:
try err
翻译后生成的代码:
if err != nil { return err}
不会调用不存在的 handler 进行解决,将会间接返回。三行(if err != nil 的逻辑)间接变 3 个单词(try)。
不想写函数,也能够间接:
x, err := f()try err, fmt.Errorf("f fail: %w", err)
针对 defer+try 的场景能够如下:
func CopyFile(src, dst string) error { defer try func(err error) error { return fmt.Errorf("copy %s %s: %w", src, dst, err) } ...}
入参是比拟灵便的,作者心愿它是泛型,这样可能适配各种场景的要求。
示例和实际
针对本提案,原作者给出了各类应用场景的示例。如下代码:
import ( "fmt")// This helper should be defined in the fmt packagefunc Handlew(format string, args ...any) func(error) error { return func(err error) error { args = append(args, err) return fmt.Errorf(format+": %w", args...) }}// This helper should be defined in the fmt packagefunc Handlef(format string, args ...any) func(error) error { return func(err error) error { args = append(args, err) return fmt.Errorf(format+": %v", args...) }}func valAndError() (int, error) { return 1, fmt.Errorf("make error")}func newGo() (int, error) { x, err := valAndError() try err // Common formatting functions will already be provided i := 2 x, err = valAndError() try err, Handlew("custom Error %d", i) // Using a custom error type // For convenience the error type can expose a method to set the error x, err = valAndError() try err, TheErrorAsHandler(i)}type TheError struct{ num int err error}func (t TheError) Error() String { return fmt.Sprintf("theError %d %v", t.num, t.err)}func TheErrorAsHandler(num int) func(err) TheError { return func(err error) TheError { return theError{ num: i, err: err } }}
另外在日常的 Go 工程中,提案作者认为 CopyFile 函数是新提案语句的一种很好的实际。为此基于 try-handler 进行了一版革新和阐明。
如下代码:
// This helper can be used with deferfunc handle(err *error, handler func(err error) error) { if err == nil { return nil } *err = handler(err)}func CopyFile(src, dst string) (err error) { defer handle(&err, func(err error) error { return fmt.Errorf("copy %s %s: %w", src, dst, err) }) r, err := os.Open(src) try err defer r.Close() w, err := os.Create(dst) try err, func(err error) error { os.Remove(dst) // only if Create fails return fmt.Errorf("dir %s: %w", dst, err) } defer w.Close() err = io.Copy(w, r) try err err = w.Close() try err return nil}
引入 try-hanlder 后,可能做到:
- 插入谬误的返回语句,进行机制预设。
- 在返回谬误之前将谬误处理函数组合在一起,便于后续的解决。
总结
在这个新提案中,一旦施行,就能够缩小如下代码的编写:
if err != nil { return ...}
在代码编写上会节俭一些行数,且能够为错误处理机制引入一些新的 ”操作“,这是该提案的劣势。
然而从 Go 开发者的角度而言,会引入一些新的副作用,例如:初学者的学习老本、Go 工具链的革新、程序了解的复杂度减少。
另外新的语句,仿佛比拟难与 Go1.13 引入的 error.Is 和 As 有较好的相关联性。如果是做一个第三方用户库引入倒能够,但若是作为规范进入 Go 源代码中,仿佛又有些心心相印(提案作者心愿进入)。
看了那么多提案,Go 错误处理机制的 ”降级“,仿佛陷入了手心手背都是肉的阶段...
文章继续更新,能够微信搜【脑子进煎鱼了】浏览,本文 GitHub github.com/eddycjy/blog 已收录,学习 Go 语言能够看 Go 学习地图和路线,欢送 Star 催更。
举荐浏览
- Go 只会 if err != nil?这是不对的,分享这些优雅的解决姿态给你!
- Go 错误处理新思路?用左侧函数和表达式
- 先睹为快,Go2 Error 的挣扎之路
Go 图书系列
- Go 语言入门系列:初探 Go 我的项目实战
- Go 语言编程之旅:深刻用 Go 做我的项目
- Go 语言设计哲学:理解 Go 的为什么和设计思考
- Go 语言进阶之旅:进一步深刻 Go 源码