大家好,我是煎鱼。
一年半前分享了《》的文章,内容涉及 Go1 错误处理的问题、Go1.13 的加强、Go2 的新错误处理提案的详解。有多少小伙伴还记得 Go2 的新错误提案的 “美好” 未来?
当时 Go2 的新提案也遭受到了不少批评,@Liam Breck 在《Golang, how dare you handle my checks![1]》中对此进行了批判,让我们一起来学习吧!
复习语法
在 2018 年 8 月,官方正式公布了 Go 2 Draft Designs[2],其中包含泛型和错误处理机制改进的初步草案:
下面是关键的 Go2 错误处理新语法。
错误处理(Error Handling)
if err != nil简单例子:
优化后的方案如下:
主函数:
checkcheck f(x, y, z)check errhandlereturn错误值打印(Error Printing)
第二个要解决的问题是错误值(Error Values)、错误检查(Error Inspection)的问题,其引申出错误值打印(Error Printing)的问题,也可以认为是错误格式化的不便利。
官方针对此提出了提出了 Error Values[4] 和 Error Printing[5] 的草案设计。
简单例子如下:
优化后的方案如下:
errors.Iserrors.As%+v对提案批判
目标较为模糊
在 Go2 新错误处理的提案和草案中,@Liam Breck 认为其没有去讨论根本的需求。仅有一个简短的目标部分,如下四点:
- 占用空间小的错误检查。
- 对开发人员友好的错误处理。
- 显式检查和处理错误。
- 保证现有 Go1 代码的兼容性。
更也没有提到未来可能的扩展性,只是纯粹的满足当下的诉求。这类是模糊的,在激发新的设计思路上有局限性。
无法统一错误处理
在真实的应用中,一个函数使用两种及以上的重复错误处理方式是非常常见的。
如下代码:
新提案中,check 和 handle 函数并不提供多种处理错误的途径。这是一个明显的遗漏,也没法统一错误处理机制。
如此的话,check 和 handle 就完全是只加了一种新的方式,让原本的错误处理机制更加的繁杂。
混用 err != nil 和 check
handle 函数是后进先出的模式,只能从一个函数中跳出。也就是说它不能很友好的处理可恢复的错误内容。
但实际上,许多方法返回错误是很常见的。因此我们需要同时使用 err!= nil 和 check,显得非常的繁琐。
如下代码:
又是 if err != nil,又是 handle,又是 check 函数。
嵌套 check 更复杂
通过 check 函数,对返回错误的函数调用进行嵌套调用,将会模糊了操作的顺序。
虽然在大多数情况下,错误发生时的调用顺序应该是清楚的,但在 check 函数下会显得不如 if err != nil 清晰。
如下代码:
另外嵌套代码会助长不可读的结构:
现在回顾一下,该语言禁止。
是不是显得有些讽刺呢?
if err != nil 太好用
Go1 的错误处理程序太友好了,也就是:
其挫败了 "提高开发人员编写错误处理程序的可能性" 的目标,它使得在没有上下文信息的情况下返回错误是很容易的。
注:个人感觉,这一点既像黑又像粉...原作者反串黑?当然,确实 if err != nil 很好上手。
复杂的错误链
对于下面的例子,看看它的感觉...
如下代码:
显然,这段代码是 “难以捉摸” 的,我们必须用眼睛解析整个函数,理解整个错误处理的流程和顺序。
将会加大我们对程序的认知负担。
总结
通过对 Go2 错误处理的设计草案的复习,我们了解到了 check 和 handle 函数的用法和思路。再针对新的语法,又对可能发生的新问题进行了 “批判”。
总的来说,新的语法,在弊端上会增加既有的代码复杂度和可读性,可以引发各种奇怪的嵌套,还会与 if err != nil 产生重复,变成了一种新的处理方式(多了一种)。
是否会还不如 if err != nil 那么的纯粹?
Go 图书系列
推荐阅读
参考资料
[1]
Golang, how dare you handle my checks!:
[2]
Go 2 Draft Designs:
[3]
Go2 error handling:
[4]
Error Values:
[5]
Error Printing: