大家好,我是煎鱼。
在 Go 这门编程语言中,if err != nil 的错误处理方式,是我们一直关注的焦点之一。所有的 Go 社区调查中,都有希望优化和改进错误处理的声音和各种想法。
春节期间刷到了一个由 @Bill Soudan 提出的新提案《proposal: Go 2: support new form of switch statement during variable assignment which jumps to function-wide case blocks[1]》,是针对错误处理优化的,思路还是有些新奇的。
图片
以往印象里没有人提过这个方式。今天分享给大家,一起围观和学习!
新提案
该提案希望在变量赋值时能够支持新的 switch 语句形式。从功能出发,更具体指的是:要支持 switch 跳转到函数范围内的任意位置的标签。
这个特性的目的是:简化繁琐又重复的 if err !=nil 的错误检查代码,也可以用于其他逻辑实现。
具体的对比例子如下。
如果是原本的 Go1 错误处理的范式。
代码如下:
func CopyFile(src, dst string) error {
r, err := os.Open(src)
if err != nil {
return err
}
defer r.Close()
w, err := os.Create(dst)
if err != nil {
return err
}
defer w.Close()
if _, err := io.Copy(w, r); err != nil {
return err
}
if err := w.Close(); err != nil {
return err
}
}
要写比较多的判断和返回错误的逻辑,并且这些代码比正式的调用代码还要多。所以也常被人戏称一个 Go 工程里 80% 都是 if err != nil 等错误检查代码。
基于本文提到的 switch-case 提案进行改造。
新的代码如下:
func CopyFile(src, dst string) error {
r, switch err := os.Open(src)
defer r.Close()
w, switch err := os.Create(dst)
defer w.Close()
_, switch dstErr := io.Copy(w, r)
switch dstErr = w.Close()
return nil
case dstErr != nil:
os.Remove(dst)
err = dstErr
fallthrough
case err != nil:
return fmt.Errorf("copy %s %s: %v", src, dst, err)
}
注意几个细节点:
- switch 关键字在对应的 err 变量前作为声明标识。
- case 关键字根据对应的 err 变量,运行不同的错误处理逻辑。
- switch-case 子句可以在同一函数内的不同位置进行调用。
这种 switch-case 的使用方式,从优点来看。确实收拢了统一的错误处理逻辑,减少了重复繁琐的代码量。
短短的代码片段,看起来像那么一回事,能一定程度上满足大家原始的诉求。
缺点的话,个人认为会增加认知和逻辑复杂度。你根本不知道 switch-case,这个 case 他的准确逻辑位置在哪里。
一旦有人套娃,就非常麻烦了。同时 switch-case 延伸出多种不同的使用方式,会产生二义性,这是一个折腾的事情。
总结
今天给大家分享了我所看到的一个 Go 错误处理的新提案,其本质上是利用 switch-case 的新语法机制,实现了 err 变量和 case 的关联。以此简化错误检查的逻辑。
软件开发是没有银弹的。如何引入更优雅的错误处理机制,且不要带过来过大的程序员心智负担,还要要确保编译器性能尚可。Go 核心团队可能是想要在这三个圈里设计一个最优的选择。
参考资料
[1]
proposal: Go 2: support new form of switch statement during variable assignment which jumps to function-wide case blocks: https://github.com/golang/go/issues/65019