Go 官方错误处理讨论:用 ? 替代 if err != nil 可以不?

开发 前端
今天我们给大家带来了 Go 核心团队的错误处理新提案,老大哥 @Ian Lance Taylor 基于 Rust 的问号操作符作为灵感,设计出了 Go 的问号操作符和块的联动机制。

大家好,我是煎鱼。

对于 Go 这一门编程语言,截至目前较大争议话题仍在 if err != nil 在 Go 应用里所带来的各种繁杂代码,引起了社区很多正反方的探讨。

原本以为 Go 核心团队已经摆烂了。但最近老大哥 @Ian Lance Taylor 提出了一个新提案[1](后转为讨论[2]),引起了大量的社区交流:

图片图片

背景:为什么还要关注这件事?

在 Go 核心团队的视野中,现阶段对于处理 Go 错误处理的动力,个人理解至少来源于以下几个主要原因,不断地推动他们看到这件事。

第一是:社区中用代码反馈最多的,也就是在现在的 Go 应用代码中,存在较多的 if err != nil 的相关代码:

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
 }
}

一堆的 if err != nil 代码。

第二是:在历年的 Go 开发人员调查报告中提示 “错误处理被列为人们今天使用的最大特定挑战”。

如下图所示:

图片图片

在调查结果中,有提到:“在封闭问题中,回答最多的是学习如何有效地编写 Go(15%)和错误处理的冗长(13%)。”

第三是:在 Go issues 中存在大量的 Go 错误处理的各类提案,例如:

图片图片

  • 《proposal: Go 2: onerr return[3]》
  • 《proposal: Go 2: add or err: statement after function calls for error handling[4]》
  • 《proposal: Go 2: Use ?variable simplify handling of multiple-return-values[5]》

太多太多太多这类提案了。我也分享过很多脑洞很大的社区错误新提案。

新提案:? 新语法

这种新语法的部分灵感来源于:Rust 的问号运算符。

? 将会吸收函数返回的错误,并在错误不为 nil 时自动返回,这与之前的 try 的提议类似。

它的不同之处在于:? 是一个显式语法元素,而不是对预先声明函数的调用,而且 ? 只能出现在语句的末尾,不能出现在表达式的中间。

基本介绍

例如,原本的 Go 错误处理代码如下:

r, err := SomeFunction()
 if err != nil {
  return fmt.Errorf("something failed: %v", err)
 }

新提案引入新的 ? 语法,将其改写为如下:

r := SomeFunction() ? {
  return fmt.Errorf("something failed: %v", err)
 }

当然。如果是另外一种原本的写法:

if err := SomeFunction2(); err != nil {
  return fmt.Errorf("something else failed: %v", err)
 }

也依然可以改写为:

SomeFunction2() ? {
  return fmt.Errorf("something else failed: %v", err)
 }

? 这个用法将会吸收函数的错误结果。它引入了一个新块,如果错误结果不为 nil,则执行该块块。

在新块中,fmt.Errorf 里的标识符 err 指的是上述吸收的错误结果。也就是当块跟在 ? 后面时,它会隐式声明一个新的 err 变量。

完整例子

func Run() error {
 Start() ? // returns error from Start if not nil
 Wait() ?  // returns error from Wait if not nil
 return nil
}
func CopyFile(src, dst string) error {
 r := os.Open(src) ? {
  return fmt.Errorf("copy %s %s: %v", src, dst, err)
 }
 defer r.Close()

 w := os.Create(dst) ? {
  return fmt.Errorf("copy %s %s: %v", src, dst, err)
 }

 io.Copy(w, r) ? {
  w.Close()
  os.Remove(dst)
  return fmt.Errorf("copy %s %s: %v", src, dst, err)
 }

 w.Close() ? {
  os.Remove(dst)
  return fmt.Errorf("copy %s %s: %v", src, dst, err)
 }
}
func MustOpen(n string) *os.File {
 f := os.Open(n) ? {
  panic(err)
 }
 return f
}
func TestFileData(t *testing.T) {
 f := os.Open("testfile") ? {
  t.Fatal(err)
 }
 ...
}
func CreateIfNotExist(name string) error {
 f, err := os.OpenFile(name, os.O_EXCL|os.O_WRONLY, 0o666)
 if errors.Is(err, fs.ErrExist) {
  return nil
 }
 err ? // returns err if it is not nil
 // write to f ...
}

社区争论

Go 错误处理机制这事,一向是正反双方都有。有支持继续保持的,也有反对的。

其中一位社区同学 @Michael Fridman 直接掏出了 Go 创始人 @Rob Pike 的演讲 PPT 语录:

图片

该位同学表示:这一提议的优点有限 -- 它增加了另一种做事的方式,并使代码更难阅读(具有讽刺意味的是)。

其真心希望谷歌不会不惜一切代价发展 Go 语言,因为这将损害 Go 的长期用户对 Go 的喜爱。

并且表示:“让我们保持 Go 的简单和无趣”

总结

今天我们给大家带来了 Go 核心团队的错误处理新提案,老大哥 @Ian Lance Taylor 基于 Rust 的问号操作符作为灵感,设计出了 Go 的问号操作符和块的联动机制。

关于 Go 的错误处理机制,我经历很多。见到各种人做封装,还有使用 pnaic+recover 来做 error 机制的。各种方法都有。谁都不服谁。

但是目前 Go1 还是陷入了一个非常尴尬的境地,有人希望改,有人不希望改。无论如何都难以 “讨好” 全部人。

接下来就看新上任的 Go 核心团队负责人的魄力如何了。能否像以前 rsc 力推 module 一样背负骂名且用力推进。

毕竟,@Ian Lance Taylor 能提出这个提案和讨论。很有可能就是内部先讨论过的了。

参考资料

[1]提案: https://github.com/golang/go/issues/71203

[2]讨论: https://github.com/golang/go/discussions/71460

[3]proposal: Go 2: onerr return: https://github.com/golang/go/issues/32848

[4]proposal: Go 2: add or err: statement after function calls for error handling: https://github.com/golang/go/issues/33029

[5]proposal: Go 2: Use ?variable simplify handling of multiple-return-values: https://github.com/golang/go/issues/33074

责任编辑:武晓燕 来源: 脑子进煎鱼了
相关推荐

2020-12-17 06:25:05

Gopanic 模式

2024-06-05 08:47:20

Go语言方式

2023-03-10 08:48:29

2014-11-17 10:05:12

Go语言

2021-04-29 09:02:44

语言Go 处理

2022-07-13 08:53:28

函数Go语言

2022-08-01 08:48:39

Go代码接口

2024-02-28 08:54:57

switchGo错误

2022-05-26 08:53:47

Go函数代码

2024-03-14 09:35:54

Go 错误select代码

2022-06-13 07:03:25

Go 语言怎么优化重

2021-09-13 07:53:31

Go错误处理

2022-09-05 08:55:15

Go2提案语法

2024-03-27 08:18:02

Spring映射HTML

2021-09-27 15:33:48

Go 开发技术

2021-09-27 23:28:29

Go多协程并发

2023-10-26 15:49:53

Go日志

2021-09-27 10:04:03

Go程序处理

2021-04-14 07:08:14

Nodejs错误处理

2022-10-24 08:55:13

Go工具链开发者
点赞
收藏

51CTO技术栈公众号