go运行时可以检测panic(nil)并报告错误.
但是,我不能检测panic(nil)用recover()的defer红色的功能,因为它返回nil,所以我不能正常执行(没有出现恐慌)区别开来,因为我将测试的返回值recover()是零.
例如,
defer func(){
var err = recover()
if err != nil {
// Real serious situation. Panic from inner code.
// And we may have some critical resources which
// must be cleaned-up at any cases.
// However, this will not be executed for panic(nil)
rollback()
// I am still not sure that how should I treat `panic`…
// Should I just ignore them?
}
}()
var err = doTransaction()
if err == nil {
commit() // Happy case.
} else {
rollback() // Regular execution. Just a lucky case.
}
Run Code Online (Sandbox Code Playgroud)
ROLLBACK只是一个例子,我想我可以有很多关键案例需要清理.好吧,那些清理代码也不会在真正的程序崩溃中执行,但我想尽可能地保护.
如何在延迟函数中检测任何恐慌,无论其参数如何?
我只需在退出前设置一个标志即可。
AFAIK,panic 是特定于 Goroutine 的,并且单个 Goroutine 保证在单线程中。因此变量周围不需要同步/锁定ok。如果我错了,请纠正我。
func clean(ok *bool) {
if *ok {
log.Printf("Execution OK. No panic detected.\n")
} else {
var reason = recover()
log.Printf("Some bad thing happen. reason = %v\n", reason)
panic("Abnormal exit. Program abandoned. Stack-trace here.")
debug.PrintStack() // Oops. this will not run.
}
}
func main() {
var ok bool = false
defer clean(&ok)
panic(nil)
test1() // Here's the main job.
ok = true
log.Printf("All work done. Quit gracefully.\n")
}
Run Code Online (Sandbox Code Playgroud)
检查提案 25448 “规范:保证恢复时返回非零值”是否有帮助。
在 Go 1 中允许使用 nil 恐慌值调用恐慌,但很奇怪。
几乎所有代码都通过以下方式检查恐慌:
Run Code Online (Sandbox Code Playgroud)defer func() { if e := recover(); e != nil { ... } }()...在 的情况下这是不正确的
panic(nil)。正确的方法更像是:
Run Code Online (Sandbox Code Playgroud)panicked := true defer func() { if panicked { e := recover() ... } }() ... panicked = false return .... panicked = false return建议:使运行时
panic函数将其恐慌值从私有的、不可分配类型的全局值提升nil为类似的runtime.NilPanic值:Run Code Online (Sandbox Code Playgroud)package runtime type nilPanic struct{} // NilPanic is the value returned by recover when code panics with a nil value. var NilPanic nilPanic
2018年提出,刚刚被接受(2023年1月)
当前的建议是从 Go 1.21(比如说)开始,
panic(nil)变成panic(&runtime.PanicNil{})whilepanic(因此恰好是接口的变量nil也可以这样做,而不仅仅是文字文本panic(nil))。
panic(nil)总是可以,但在更改后返回而不是recover()返回。 如果,则此更改被禁用并导致像往常一样返回。&runtime.PanicNil{}nilGODEBUG=panicnil=1panic(nil)recover()nil假设#56986(“提案:Go 的扩展向后兼容性”)也发生在 Go 1.21 中,这种行为只会在
go 1.21工作模块(顶级go.mod)中的“ ”模块中发生变化。
因此,当您从 Go 1.20 更新到 Go 1.21 时,不更改任何go.mod行,您仍然会得到旧的panic(nil)行为。
当您将顶级 go.mod 更改为 时go 1.21,您就会在整个程序中获得新的行为。
除非我误解了你的问题,否则即使传递的值是 ,延迟函数调用也会nil在恐慌时运行。下面的程序说明了这一点:
package main
import "fmt"
func main() {
defer func() {
fmt.Println("Recover:", recover())
}()
panic(nil)
}
Run Code Online (Sandbox Code Playgroud)
因此,您可以通过比较topanic(nil)返回的值轻松检测是否发生。recover()nil
编辑回答评论:
是的,这是真的; 延迟调用通常会在函数返回时运行。但它们也会在panic().
问题更新后编辑:
你是对的,没有办法区分这些情况。另一方面,恐慌也nil没有多大意义——特别是因为这个限制。
我能想到的唯一用例panic(nil)是故意避免恢复并通过堆栈跟踪强制程序崩溃。不过,还有更优雅的方法可以做到这一点,runtime例如使用包。