共计 3224 个字符,预计需要花费 9 分钟才能阅读完成。
大家好,我是煎鱼。
在近日新的 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 package
func 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 package
func 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 defer
func 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 源码