我应该使用恐慌还是返回错误?

lai*_*e9m 19 error-handling go panic

Go提供了两种处理错误的方法,但我不确定使用哪种方法.

假设我正在实现一个ForEach接受切片或地图作为参数的经典函数.要检查是否传入了iterable,我可以这样做:

func ForEach(iterable interface{}, f interface{}) {
    if isNotIterable(iterable) {
        panic("Should pass in a slice or map!")
    }
}
Run Code Online (Sandbox Code Playgroud)

要么

func ForEach(iterable interface{}, f interface{}) error {
    if isNotIterable(iterable) {
        return fmt.Errorf("Should pass in a slice or map!")
    }
}
Run Code Online (Sandbox Code Playgroud)

我看到一些讨论说panic()应该避免,但人们也说如果程序无法从错误中恢复,你应该panic().

我应该使用哪一个?选择正确的主要原则是什么?

Adr*_*ian 30

来自Dave Cheney:

panics对你的程序来说总是致命的.在惊慌中你永远不会认为你的来电者可以解决问题.因此panic,仅用于特殊情况,代码无法使用的情况,或任何集成代码的人继续使用.

你应该假设对于整个程序,或者至少对于当前的goroutine来说,恐慌将立即致命.问问自己"当这种情况发生时,应用程序是否会立即崩溃?" 如果是,请使用恐慌; 否则,使用错误.

  • 此回复不是答案:它是一个问题(*问问自己...*)。 (4认同)
  • 同意。但如何判断程序是否可以继续呢?对我来说,似乎如果不小心传入某个整数,程序就无法继续运行。 (2认同)
  • 可能是这样。也许“可以继续”有点含糊-真正的含义是,如果发生错误,您的程序有什么办法可以处理错误?一般来说,在不确定的情况下,返回时会返回错误。恐慌应该很少见。 (2认同)
  • 完整阅读。它鼓励读者问自己一个问题,以便根据答案提供逻辑以自己做出决定。 (2认同)
  • 同时,来自官方博客的 [post](https://blog.golang.org/defer-panic-and-recover) 提出了一种不同的方法。他们以 [json 库](https://golang.org/src/encoding/json/decode.go) 为例,它在内部使用了 `panic` 并将其转换为公共 API 中的错误。 (2认同)

dol*_*men 12

使用panic.

因为您的用例是捕获 API 的不当使用。如果程序正确调用您的 API,则在运行时永远不会发生这种情况。

事实上,如果测试被删除,任何使用正确参数调用 API 的程序都会以相同的方式运行。测试只是在早期失败并显示有助于犯错误的程序员的错误消息。理想情况下,在开发过程中运行测试套件时可能会出现一次恐慌,程序员甚至会在提交错误代码之前修复调用,并且错误使用永远不会到达生产环境。

另请参阅对问题的回复是否使用错误进行函数参数验证是 Go 中的一个好模式?.

  • Go 自己的博客指出“[Go 库中的约定是,即使包在内部使用恐慌,其外部 API 仍然会显示显式错误返回值](https://go.dev/blog/defer-panic-and-recover )”所以你应该使用错误而不是恐慌。另外,您使用恐慌的逻辑有些缺陷;如果正确调用,则永远不会发生这种情况,就像说网站应该停止发送 404 错误并开始使浏览器崩溃,因为如果人们正确输入 URL,则不应发出 404 错误。如果有的话,API 和库需要更加容忍使用中的错误。 (4认同)
  • 我不认为指针是一个问题,因为您不应该允许可以显式声明服务器上内存中的点的 API 请求。无论如何,您有需要满足的请求/响应合同,否则这是无效的请求。这应该在您的解组/解析函数中得到处理。这里的错误会响应无效的请求;没有理由恐慌。如果你允许 nil 那么它就不是恐慌或抛出错误的理由。 (2认同)

yur*_*s87 6

我喜欢它在一些库中完成的方式,在常规方法之上DoSomething,它的“panicky”版本添加了MustDoSomething. 我对 相对较新go,但我已经在几个地方看到过它,尤其是sqlx
一般来说,如果您想将代码公开给其他人,您应该拥有Must-该方法的常规版本,或者您的方法/函数应该让客户有机会恢复他们想要的方式,因此error应该可供他们使用以一种go惯用的方式。
话虽如此,我同意如果你的 API/库使用不当,恐慌也是可以的。事实上,我也见过这样的方法,MustGetenv()如果缺少关键的 env.var,就会出现恐慌。基本上是快速失败机制。