Go臭名昭著的错误处理引起了编程语言外部人士的注意,它常常被吹捧为该语言最值得怀疑的设计决策之一。如果你研究一下用Go编写的Github上的任何项目,几乎可以保证你会比代码库中的任何其他项目更频繁地看到这些代码行:

尽管对于那些不熟悉Go语言的人来说,这似乎是多余的和不必要的,但Go中的错误被视为一等公民(价值观)的原因在编程语言理论和Go作为一种语言本身的主要目标中有着根深蒂固的历史。为了改进Go如何处理错误,已经做出了许多努力,但到目前为止,有一项提议胜于所有其他提案:

if err != nil!

Go的错误设计哲学

Go关于错误处理的哲学迫使开发人员将错误作为他们编写的大多数函数的第一类公民。即使您忽略了错误,使用的内容如下:

大多数linter或IDE都会发现您忽略了一个错误,并且在代码评审期间,您的团队成员肯定可以看到它。但是,在其他语言中,可能不清楚您的代码没有处理try-catch代码块中的潜在异常,在处理控制流方面完全不透明。

如果您以标准方式使用Go的错误处理,您将获得以下好处:

func f() (value, error)
err!=nil
err != nil

其他语言处理方式:抛出异常

Javascript Node.jsthrowing exceptions

如果这些函数中的任何一个发生错误,则错误的堆栈跟踪将在运行时弹出并记录到控制台,但不会对发生的问题进行明确的代码逻辑处理。

您的criticalOperation函数不需要显式处理错误流,因为在try块中发生的任何异常都将在运行时引发,并给出错误原因的堆栈跟踪。与Go相比,基于异常的语言的一个优点是,即使发生未处理的异常,在运行时仍会通过堆栈跟踪引发未处理的异常。在Go中,可能根本不用处理严重错误,这可能会更糟。Go为您提供了对错误处理的完全控制,但也提供了全部责任。

另外异常绝对不是其他语言处理错误的唯一方法。例如,Rust很好地折衷了使用选项类型和模式匹配来查找错误条件,并利用一些不错的语法糖来达到类似的效果。

为什么Go不使用异常进行错误处理

Go设计之禅

Go的禅宗提到了两个重要的哲理:

if err != nilvalue, errortry catch

基于异常的代码通常是不透明的

使用基于异常的代码,您将不得不意识到在每种情况下您的代码都可能在没有实际处理异常的情况下出现异常,因为它们会被您的try catch块捕获。也就是说,它鼓励程序员从不检查错误,至少知道,某些异常(如果发生)将在运行时自动处理。用基于异常的编程语言编写的函数通常如下所示:

此代码不会确保异常得到正确处理。让上面的代码意识到异常之间的区别可能是切换到saveToDB(item)内部进行异常捕获处理,但是下一句代码item.Text仍然执行,这是不透明的,难以解释,并可能鼓励一些懒惰的编程习惯。在函数式编程术语中,这被称为花哨术语:违反引用透明性。这篇来自2005年微软工程博客的博文至今仍然有效,即:

我的意思不是说异常不好。我的观点是,异常太难了,我不够聪明,无法处理它们。

Go error语法的好处

轻松创建可行的错误链

一个超级系统的模式,如果error!=nil是允许容易的错误链遍历程序的层次结构,一直到需要处理的地方。例如,由程序的main函数处理的常见Go错误可能如下所示:

已经足够清晰应用程序有错误。这样的错误不是因为一个不可读的、神秘的堆栈跟踪而崩溃,而是由于我们可以添加人类可读上下文的因素导致的,应该通过上面所示的清晰的错误链来处理异常问题。

此外,这种错误链自然会作为标准Go程序结构的一部分而出现,可能看起来像这样:

fmt.Errorf("something went wrong: %w", err)
github.com/pkg/errorserrors.Wrapf(err, "could not save user with email %s", email)
  • 为您的错误添加可用于开发人员时堆栈跟踪
  • 对返回的错误做点什么,不要只是把它们放到main上,记录下来,然后忘记它们
  • 保持您的错误链明确

当我编写Go代码时,错误处理是我永远不会担心的一件事,因为错误本身是我编写的每个函数的核心问题,从而使我能够完全控制我如何安全、可读且负责任地处理它们。

if err!= nil

推荐