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

Go 我应该使用恐慌还是返回错误?,go,error-handling,panic,Go,Error Handling,Panic,Go提供了两种处理错误的方法,但我不确定使用哪种方法 假设我正在实现一个经典的ForEach函数,它接受一个切片或映射作为参数。要检查是否传入了iterable,我可以执行以下操作: func ForEach(iterable interface{}, f interface{}) { if isNotIterable(iterable) { panic("Should pass in a slice or map!") } } 或 我看到一些讨论说应该避免pa

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

假设我正在实现一个经典的
ForEach
函数,它接受一个切片或映射作为参数。要检查是否传入了iterable,我可以执行以下操作:

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

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


我应该用哪一个?那么,选择正确的方案的主要原则是什么呢?

您应该假设,对于整个程序,或者至少对于当前的goroutine,恐慌将立即致命。问问自己“当这种情况发生时,应用程序是否应该立即崩溃?”如果是,请惊慌失措;否则,请使用错误。

恐慌通常意味着出现意外错误。主要用于在正常操作期间不应发生的错误上快速失败,或者我们不准备优雅地处理这些错误。因此,在这种情况下,只需返回错误,您就不希望程序死机

对于正常的错误处理,不要使用死机。使用错误和多个返回值。请参阅。

如果在启动服务时没有提供或没有提供某些强制要求(例如数据库连接、某些必需的服务配置),则应使用panic


任何用户响应或服务器端错误都应该有返回错误。

使用
panic

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

事实上,如果删除测试,任何使用正确参数调用API的程序都将以相同的方式运行。测试只会提前失败,并显示一条对出错的程序员有用的错误消息。理想情况下,在开发过程中,当运行testsuite时,可能会出现一次恐慌,程序员甚至在提交错误代码之前就会修复调用,而这种不正确的使用将永远不会影响生产


另请参见问题“使用错误验证函数参数是否是Go中的良好模式?”

问问自己以下问题:

  • 无论你的应用程序编写得有多好,你是否希望出现异常情况?作为应用程序正常使用的一部分,您认为让用户了解这种情况应该有用吗?将其作为错误处理,因为它关系到应用程序是否正常工作
  • 如果您编写了适当的代码(并且有些防御性),那么这种异常情况是否不会发生?(例如:除以零,或访问数组元素超出范围)在该错误下,您的应用程序是否完全不知道?恐慌
  • 您是否拥有API并希望确保用户正确使用它?恐慌。如果使用不当,API将很少恢复

我喜欢在一些库中这样做,在常规方法
DoSomething
的基础上,它的“恐慌”版本添加了
MustDoSomething
。我对
go
比较陌生,但我已经在好几个地方见过了,尤其是。
一般来说,如果您想向其他人公开您的代码,您应该拥有
Must-
和该方法的常规版本,或者您的方法/函数应该让客户机有机会以他们想要的方式恢复,因此
error
应该以
go
惯用的方式提供给他们。

话虽如此,我同意如果您的API/库使用不当,也可以惊慌失措。事实上,我也看到过类似
MustGetenv()
的方法,如果缺少一个关键的env.var,这些方法会导致恐慌。基本上是快速失败机制。

相关:(不是这个问题的确切答案,但这里也有一些推理。)对于验证使用错误,这里您要检查
isNotIterable(iterable)
。错误是合适的。经验法则:在acual
main
函数中,可以使用panics。所有其他情况下的错误。在其他几个程序中使用的库中,确实“禁止”使用恐慌。@RickyA介意发布一个答案吗?相关:(真的是同一个问题)同意。但我如何确定该计划是否可以继续?对我来说,如果某个整数被意外传入,程序将无法继续运行。可能就是这样。也许“可以继续”有点模糊——它真正的意思是,如果错误发生了,你的程序有没有办法处理错误?一般来说,在go中,如果您不确定,则返回一个错误。恐慌应该是非常罕见的。这个回答不是一个答案:这是一个问题(问你自己…),请完整阅读。它鼓励读者问自己一个问题,以提供基于答案的逻辑来为自己做出决定。同时,来自官方博客的一篇文章提出了一种不同的方法。他们以为例,在内部使用
panic
,并将其转换为公共API中的错误。定义“正常操作”。因为这就是问题的关键:“检查
是否不适合正常操作?”也是有意义的。
func ForEach(iterable interface{}, f interface{}) error {
    if isNotIterable(iterable) {
        return fmt.Errorf("Should pass in a slice or map!")
    }
}