recover 只能在 defer 函数中生效,且仅对同 goroutine 的 panic 有效;直接调用、非 defer 中调用或跨 goroutine 均返回 nil,需满足三前提:defer 内调用、panic 前已注册、同 goroutine。

recover 只能在 defer 函数中生效,且仅对当前 goroutine 的 panic 有效——它不是全局异常处理器,也不是错误兜底方案。
为什么直接调用 recover 总是返回 nil
常见错误是把 recover 放在普通函数里、或放在 defer 外层调用,比如:
func badExample() {
recover() // 永远 nil,不在 defer 中,也不在 panic 后的恢复期
panic("boom")
}
真正起作用的前提有三个,缺一不可:
-
recover()必须写在defer声明的函数体内 - 该
defer必须在 panic 发生前已注册(即 panic 前执行过defer语句) - panic 必须发生在同一 goroutine 内;跨 goroutine 的 panic 无法被本 goroutine 的
recover捕获
recover 在 defer 中的典型写法
最简可用模式是:在函数入口注册一个匿名 defer,内部立即调用 recover 并处理返回值:
func safeDiv(a, b int) (result int, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("panic recovered: %v", r)
}
}()
return a / b, nil
}
注意几个实操细节:
- 必须用
defer func() { ... }()形式,不能写成defer recover()(语法错误,recover不是可调用表达式) - 如果想获取 panic 的原始值(比如自定义错误),直接类型断言即可:
r.(error)或r.(string),但务必先判断r != nil - 不要在多个
defer中重复调用recover——只有最靠近 panic 的那个(即最后执行的defer)能捕获到,其余都拿到nil
recover 无法捕获的场景和替代思路
recover 对以下情况完全无效:
- 程序被系统信号终止(如
SIGKILL、SIGQUIT) - 运行时致命错误(如栈溢出、内存耗尽、
fatal error: all goroutines are asleep) - 其他 goroutine 中发生的 panic(比如子协程 panic,主 goroutine 的
recover看不见)
若需跨 goroutine 错误传递,应改用显式通信机制:
- 通过
chan error接收子协程错误 - 用
sync.WaitGroup+errgroup.Group统一收集 panic 和 error - 对关键服务,用
http.Server的ErrorLog或自定义panic日志钩子(如debug.SetPanicOnFault(true)配合信号 handler)
真正难的不是写对 recover,而是判断「该不该 recover」:业务逻辑错误该用 return err,资源泄漏该靠 defer 清理,只有极少数边界场景(如插件系统、模板渲染、RPC 请求处理)才需要主动 recover 并降级。多数时候,让 panic 快速暴露问题,比掩盖它更安全。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











