Go提供了两种处理错误的方法,但我不确定使用哪一种。
假设我正在实现一个经典的ForEach
函数,它接受一个slice或一个map作为参数。要检查是否传入了一个iterable,我可以这样做:
func ForEach(iterable interface{}, f interface{}) {
if isNotIterable(iterable) {
panic("Should pass in a slice or map!")
}
}
或
func ForEach(iterable interface{}, f interface{}) error {
if isNotIterable(iterable) {
return fmt.Errorf("Should pass in a slice or map!")
}
}
我看到一些讨论说应该避免panic()
,但人们也说,如果程序不能从错误中恢复,你应该panic()
。
我应该使用哪一个?选择正确的方法的主要原则是什么?
9条答案
按热度按时间qltillow1#
你应该假设一个panic会立即对整个程序,或者至少对当前的goroutine是致命的,问问自己“当这个发生时,应用程序应该立即崩溃吗?”如果是,就使用panic;否则,使用错误。
whlutmcx2#
使用
panic
。因为你的用例是捕获API的错误使用。如果程序正确调用API,那么这种情况永远不会在运行时发生。
事实上,任何使用正确参数调用API的程序都会以相同的方式运行 * 如果测试被删除 *。测试只是在早期失败,并向出错的程序员提供错误消息。理想情况下,在开发期间运行测试套件时可能会出现一次panic,程序员甚至会在提交错误代码之前修复调用。而不正确的使用永远不会达到生产。
另请参阅this reponse来提问 Is function parameter validation using errors a good pattern in Go?。
avwztpqn3#
我喜欢在一些库中这样做的方式,在常规方法
DoSomething
的顶部,它的“panicky”版本被添加了MustDoSomething
。我对go
相对较新,但我已经在几个地方看到过它,特别是sqlx
。一般来说,如果你想将你的代码公开给其他人,你应该有
Must-
和一个常规版本的方法,或者你的方法/函数应该给予客户端一个机会来恢复他们想要的方式,所以error
应该以go
惯用的方式对他们可用。话虽如此,我同意如果你的API/库使用不当,也可以恐慌。事实上,我也见过像
MustGetenv()
这样的方法,如果缺少关键的env.var,就会恐慌。基本上是快速故障机制。ecr0jaav4#
如果在启动服务时没有提供一些强制性要求(例如,数据库连接,一些必需的服务配置),那么您应该使用panic。
任何用户响应或服务器端错误都应该返回错误。
ca1c2owp5#
问自己这些问题:
8ulbf1ek6#
尽量使用error
只有在代码可能以一种很容易崩溃的糟糕状态结束时才使用panic;确实出乎意料。上面的
ForEach()
示例是一个导出的func,它接受一个接口,所以它应该预料到有人会不正确地调用它。如果它被不正确地调用,你知道为什么你不能继续,你知道如何处理这个错误。isNotIterable
实际上是二进制的,很容易控制。但error不像try/catch
即使你试图通过查看其他语言的throw/catch来证明panic/recover的合理性,你仍然会使用错误。我们知道你正在尝试函数,因为你正在调用它,我们知道有一个错误,因为
err != nil
,就像检查抛出的异常类型一样,你可以检查errors.Is(err, ErrNotIterable)
返回的错误类型。那么并发出错应该使用panic吗?
错误仍然是Go语言中的首选方式,你可以使用一个等待组来关闭goroutines:
即使使用原始示例的结构,您仍然可以更好地控制错误而不是panic:
但是error感觉很冗长
是的,这就是问题所在。当错误被返回时,就应该在那个时候做些什么。你是在给调用代码提供选项,而不是决定开始关闭并杀死应用程序除非你
recover()
。如果你只是在调用堆栈的所有位置返回相同的错误,那么error看起来就不如panic,但这是由于没有在问题发生时加以解决。什么时候使用panic?
当你的代码在碰撞过程中崩溃,你不能假设你的出路。另一种情况是当代码假设的东西不再是真的,从这里开始必须检查每个函数的完整性将是乏味的(并可能影响性能)。尽管如此,你只会使用
panic()
来摆脱不确定性的层次...然后仍然处理错误:但如果你还是不服气……这里是Go常见问题解答
我们认为,将异常耦合到控制结构中,就像try-catch-finally习惯用法那样,会导致代码复杂,也会鼓励程序员将太多的普通错误(如无法打开文件)标记为异常。
Go采用了不同的方法。对于普通的错误处理,Go的多值返回使得报告错误变得很容易,而不会重载返回值。规范的错误类型,加上Go的其他功能,使错误处理变得令人愉快,但与其他语言截然不同。
vddsk6oq7#
panic通常意味着发生了意外错误。通常用于在正常操作期间不应该发生的错误上快速失败,或者我们没有准备好优雅地处理错误。因此在这种情况下,只需返回错误,您不希望程序出现panic。
h9vpoimq8#
我认为前面的答案都不正确:
更正式地说,我们的“图灵机”坏了,我们需要回到“稳定状态”或“重置状态”。
例如,在web(微)服务中,这意味着返回40 X错误(由用户输入引起的死机)或50 X错误(由其他东西引起的死机-硬件,网络,Assert错误...)
总而言之,err返回值大多是一个错误的想法,即使GO社区已经将其作为一种宗教。使用错误返回值只是一种加速程序执行的修补方法,因为它需要更少的CPU指令来实现,但大多数时候,除了低级服务,它是无用且会促进脏代码。(注意,GO被设计为将那些低级服务实现为“easy-C”,但它被用于高级(7级)应用程序,当错误必须快速失败以避免继续未定义的状态时,这种状态可能会导致金钱损失或致命的伤亡。
更新2023-03:(添加Erlang参考)没有固执己见或惯用的方法来处理错误。有正确的方法和不正确的方法来处理错误。正确的方法是Erlang方法(设计为支持~100%的正常运行时间的语言)。C&P来自intro
blpfk2vs9#
不要使用panic来处理正常的错误。使用error和多个返回值。请参阅https://golang.org/doc/effective_go.html#errors。