recover必须在defer中调用才有效,仅在panic发生且当前goroutine的defer链执行时返回panic值,否则返回nil;它不回滚副作用,仅实现goroutine软着陆。
直接在普通函数体里写 recover() 永远返回 nil,它只在 panic 正在发生、且当前 goroutine 的 defer 链正在执行时才有意义。Go 运行时会把 panic 的值“传递”给正在执行的 defer 函数中的 recover() 调用。
defer 使用,典型模式是:defer func() {
if r := recover(); r != nil {
// 处理 panic 值
log.Printf("panic recovered: %v", r)
}
}()recover(),或者调用位置不在 panic 触发之后(比如提前 return),就捕获不到recover() 返回的是任意类型的 panic 值,不是错误对象。如果你用 panic("oops"),recover 得到的是 string;如果用 panic(errors.New("db fail")),得到的是 *errors.errorString;甚至可能是自定义结构体。
error 类型,直接转成 error 可能 panic:if err := recover().(error); err != nil { ... } // 危险!类型断言失败会再 panicr := recover()
switch x := r.(type) {
case string:
log.Printf("panic string: %s", x)
case error:
log.Printf("panic error: %v", x)
default:
log.Printf("panic unknown type: %T, value: %v", x, x)
}nil 本身不能被 recover 捕获 —— panic(nil) 会导致程序直接崩溃,recover() 无法拦截recover 只是让 goroutine 从 panic 状态中“软着陆”,继续执行 defer 后的代码,但它不撤销任何已发生的副作用。
一个函数里可以有多个 defer,它们按后进先出(LIFO)顺序执行;每个 defer 里的 recover() 都有机会捕获 panic,但只有第一个成功调用的能拿到 panic 值,后续都返回 nil。
func f() {
defer func() {
if r := recover(); r != nil {
log.Println("recovered")
}
}()
defer func() {
if r := recover(); r != nil {
log.Println("also recovered?") // 永远不会执行
}
}()
panic("boom")
}recover 不是 try/catch,它没有“异常传播”概念,也没有 finally 语义。真正难的从来不是写那行 recover(),而是判断哪里该用、哪里不该用,以及 panic 之后你还敢信任哪些数据。