大家好,我是煎鱼。

一年半前分享了《先睹为快,Go2 Error 的挣扎之路》的文章,内容涉及 Go1 错误处理的问题、Go1.13 的加强、Go2 的新错误处理提案的详解。有多少小伙伴还记得 Go2 的新错误提案的 “美好” 未来?

当时 Go2 的新提案也遭受到了不少批评,@Liam Breck 在《Golang, how dare you handle my checks![1]》中对此进行了批判,让我们一起来学习吧!

复习语法

在 2018 年 8 月,官方正式公布了 Go 2 Draft Designs[2],其中包含泛型和错误处理机制改进的初步草案:

Go 2 Draft Designs

下面是关键的 Go2 错误处理新语法。

错误处理(Error Handling)

第一个要解决的问题就是大量 if err != nil 的问题,针对此提出了 Go2 error handling[3] 的草案设计。

简单例子:

if err != nil {return err
}

优化后的方案如下:

func CopyFile(src, dst string) error {handle err {return fmt.Errorf("copy %s %s: %v", src, dst, err)}r := check os.Open(src)defer r.Close()w := check os.Create(dst)handle err {w.Close()os.Remove(dst) // (only if a check fails)}check io.Copy(w, r)check w.Close()return nil
}

主函数:

func main() {handle err {log.Fatal(err)}hex := check ioutil.ReadAll(os.Stdin)data := check parseHexdump(string(hex))os.Stdout.Write(data)
}

该提案引入了两种新的语法形式,首先是 check 关键字,其可以选中一个表达式 check f(x, y, z)check err,其将会标识这是一个显式的错误检查。

其次引入了 handle 关键字,用于定义错误处理程序流转,逐级上抛,依此类推,直到处理程序执行 return 语句,才正式结束。

错误值打印(Error Printing)

第二个要解决的问题是错误值(Error Values)、错误检查(Error Inspection)的问题,其引申出错误值打印(Error Printing)的问题,也可以认为是错误格式化的不便利。

官方针对此提出了提出了 Error Values[4] 和 Error Printing[5] 的草案设计。

简单例子如下:

if err != nil {return fmt.Errorf("write users database: %v", err)
}

优化后的方案如下:

package errorstype Wrapper interface {Unwrap() error
}func Is(err, target error) bool
func As(type E)(err error) (e E, ok bool)

该提案增加了错误链的 Wrapping Error 概念,并同时增加 errors.Iserrors.As 的方法,与前面说到的 Go1.13 的改进一致,不再赘述。

Go1.13 没有实现 %+v 输出调用堆栈的需求(没有调用栈,排查问题会很苦恼),因为此举会破坏 Go1 兼容性和产生一些性能问题,大概会在 Go2 加入。

对提案批判

目标较为模糊

在 Go2 新错误处理的提案和草案中,@Liam Breck 认为其没有去讨论根本的需求。仅有一个简短的目标部分,如下四点:

  • 占用空间小的错误检查。

  • 对开发人员友好的错误处理。

  • 显式检查和处理错误。

  • 保证现有 Go1 代码的兼容性。

更也没有提到未来可能的扩展性,只是纯粹的满足当下的诉求。这类是模糊的,在激发新的设计思路上有局限性。

无法统一错误处理

在真实的应用中,一个函数使用两种及以上的重复错误处理方式是非常常见的。

如下代码:

// 方式 1
{ debug.PrintStack(); log.Fatal(err) }// 方式 2
{ log.Println(err) }// 方式 3
{ if err == io.EOF { break } }// 方式 4
{ conn.Write([]byte("oops: " + err.Error())) } // network server

新提案中,check 和 handle 函数并不提供多种处理错误的途径。这是一个明显的遗漏,也没法统一错误处理机制。

如此的话,check 和 handle 就完全是只加了一种新的方式,让原本的错误处理机制更加的繁杂。

混用 err != nil 和 check

handle 函数是后进先出的模式,只能从一个函数中跳出。也就是说它不能很友好的处理可恢复的错误内容。

但实际上,许多方法返回错误是很常见的。因此我们需要同时使用 err!= nil 和 check,显得非常的繁琐。

如下代码:

handle err { ... }
v, err := f()
if err != nil {if isBad(err) {check err}// recovery code
}

又是 if err != nil,又是 handle,又是 check 函数。

嵌套 check 更复杂

通过 check 函数,对返回错误的函数调用进行嵌套调用,将会模糊了操作的顺序。

虽然在大多数情况下,错误发生时的调用顺序应该是清楚的,但在 check 函数下会显得不如 if err != nil 清晰。

如下代码:

check step4(check step1(), check step3(check step2())

另外嵌套代码会助长不可读的结构:

f1(v1, check f2(check f3(check f4(v4), v3), check f5(v5))

现在回顾一下,该语言禁止。

f(t ? a : b) 和 f(a++)

是不是显得有些讽刺呢?

if err != nil 太好用

Go1 的错误处理程序太友好了,也就是:

if err != nil {...
}

其挫败了 "提高开发人员编写错误处理程序的可能性" 的目标,它使得在没有上下文信息的情况下返回错误是很容易的。会降低对 handle、check 函数的使用频率,变成一个可有可无的东西。

注:个人感觉,这一点既像黑又像粉...原作者反串黑?当然,确实 if err != nil 很好上手。

复杂的错误链

对于下面的例子,看看它的感觉...

如下代码:

func f() error {handle err { return ... }           // finally thisif ... {handle err { ... }               // not thatfor ... {handle err { ... }            // nor that...}}handle err { ... }                  // secondly this...if ... {handle err { ... }               // not that...} else {handle err { ... }               // firstly thischeck thisFails()                // trigger}
}

显然,这段代码是 “难以捉摸” 的,我们必须用眼睛解析整个函数,理解整个错误处理的流程和顺序。

将会加大我们对程序的认知负担。

总结

通过对 Go2 错误处理的设计草案的复习,我们了解到了 check 和 handle 函数的用法和思路。再针对新的语法,又对可能发生的新问题进行了 “批判”。

总的来说,新的语法,在弊端上会增加既有的代码复杂度和可读性,可以引发各种奇怪的嵌套,还会与 if err != nil 产生重复,变成了一种新的处理方式(多了一种)。

是否会还不如 if err != nil 那么的纯粹?

推荐阅读

  • goto 语句让 Go 代码变成意大利面条?

  • Go 只会 if err != nil?这是不对的,分享这些优雅的处理姿势给你!

参考资料

[1]

Golang, how dare you handle my checks!: https://mnmnotmail.medium.com/golang-how-dare-you-handle-my-checks-d5485f991289

[2]

Go 2 Draft Designs: https://go.googlesource.com/proposal/+/master/design/go2draft.md

[3]

Go2 error handling: https://github.com/golang/proposal/blob/master/design/go2draft-error-handling-overview.md

[4]

Error Values: https://github.com/golang/proposal/blob/master/design/go2draft-error-values-overview.md

[5]

Error Printing: https://github.com/golang/proposal/blob/master/design/go2draft-error-printing.md

关注和加煎鱼微信,

获取一手业内消息和知识,拉你进交流群

对 Go2 错误处理提案的批判相关推荐

  1. 任正非:为什么要自我批判?

    今天研发系统召开几千人大会,将这些年由于工作不认真.BOM填写不清.测试不严格.盲目创新造成的大量废料作为奖品发给研发系统的几百名骨干,让他们牢记.之所以搞得这么隆重,是为了使大家刻骨铭记,一代一代传 ...

  2. 三十八、为什么要自我批判

    刊号:第109期(2000.9.22) 标题:1版:为什么要自我批判--在中研部将呆死料作为奖金.奖品发给研发骨干大会上的讲话 作者:任正非 内容: 今天研发系统召开几千人大会,将这些年由于工作不认真 ...

  3. 你真的了解泛型 Generic 嘛?

    泛型 Generic Programming[1] 通常指允许程序员在强类型程序设计语言中,编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型,即类型参数化 首先我们不是科班讨论学术, ...

  4. 学会提问———阅读笔记

    推荐词:在阅读这本书之前,我常常面临一个困惑,那就是在日常上课以及听讲座亦或是在与人交流的时候如何能够切实有效的提出一些高质量的问题呢?在发现这本书之后,我想我解答了自己的这个疑惑.这本书教授了我们批 ...

  5. 不合格机器人工程讲师为何不分享成功的案例

    不合格机器人工程讲师如何坦然面对失败 除了失败,更多的失败,也并非一无所获. 博客分享过,但是关注度(浏览量)不高,大部分成功案例都是学生/毕业生自身努力的结果,教育引导的作用小于他们自身的内在动力, ...

  6. 【考研英语】态度题/情感词汇

    两类文章: 开头给出错误观点 作者去批判它(反对这个观点) 开头给出观点 有很多人反对它,给出了很多反对意见 作者批评这些反对意见(支持观点,肯定态度) 具体词汇: 第一类 积极类 amazed 吃惊 ...

  7. electron 进程

    什么是进程 从计算机的角度理解,进程就是正在使用cpu的一段程序. 从编写一个electron程序的角度理解,进程就是一个窗口. 主进程和渲染进程 主进程是负责管理所有的窗口以及对应的渲染进程.怎么理 ...

  8. 举例说明如何在Go中使用Context | Gopher Daily (2021.09.15) ʕ◔ϖ◔ʔ

    每日一谚:Concurrency is not just for doing more things faster. It's for writing better code. Go技术生态 那些没有 ...

  9. Hyperledger Fabric 2.0 官方文档中文版 第3章 关键概念

    Hyperledger Fabric 2.0 官方文档中文版 第3章 关键概念 总目录 3.关键概念 引言 什么是区块链? 区块链为什么有用? 什么是Hyperledger Fabric? Hyper ...

  10. 从泥坑里爬起来的人就是圣人

                                    ――任总在核心网产品线表彰大会上的讲话 2008年9月2日 20多年的奋斗实践,使我们领悟了自我批判对一个公司的发展有多么的重要.如果我 ...

最新文章

  1. android编译的tool版本有多少,macOS Mojave(10.14.2)系统上编译LunarG/VulkanTools工程的Android版本...
  2. Java调用.Net的web service的几种方式
  3. 颠覆:链表在删除和插入的效率一定优于数组吗?
  4. vue给组件传递不同的值
  5. Non-zero CodeForces - 1300A
  6. 位置指纹法的实现(KNN)
  7. php数组 函数,PHP array_uintersect_uassoc() 函数
  8. [NOIP2005] 提高组 洛谷P1051 谁拿了最多奖学金
  9. Android笔记 codeUI与html UI
  10. PHP5.5+Nginx1.9
  11. 在git 2.13之前,只保存多个已更改的文件中的一个文件
  12. Leetcode 142. Linked List Cycle IIJAVA语言
  13. js中的4种函数调用模式:函数调用、方法调用、构造器调用、间接调用
  14. winform中导入excel表格
  15. mysql使用join和不使用join_在SQL或MySQL中不使用JOIN关键字的联接有问题吗?
  16. 计算机一级插入页眉,计算机一级考试,设置页眉为“汉字的交换码”
  17. Win7-旗舰版-连接共享打印机时,报 0x00000bcb 错误
  18. kali系统添加开机启动项
  19. python批量查询ip归属地_python查询ip归属地
  20. java流浪救助站公益志愿者管理系统

热门文章

  1. iphone查看python文件_通过python获取苹果手机备份文件中的照片,视频等信息采集...
  2. 5773. 插入后的最大值
  3. 苹果应用商店AppStore审核规则指南
  4. python搜索关键词的公众号文章标题和路径_微信文章关键词爬虫教程
  5. [DDC]Deep Domain Confusion: Maximizing for Domain Invariance
  6. ExtJs4 笔记(2) ExtJs对js基本语法扩展支持
  7. [4G5G专题-4]:RRU 全面了解什么是4G+5G RF静态射频共享?
  8. BTA | 量子链帅初:区块链的开发很漫长,技术突破要有十年心理预期
  9. html语言怎么给字体加颜色代码,html怎么给字体设置颜色
  10. 1007 素数对猜想 (20 分)(打表)