Go 中 recover 必须在 defer 函数体内调用才有效,直接在普通函数中调用始终返回 nil;正确写法是 defer 匿名函数并在其中调用 recover 处理 panic。

Go 里没有 try-catch,recover 必须配合 defer 使用
Go 不支持传统 try-catch 语法,recover 是唯一能“捕获” panic 的机制,但它**只能在 defer 函数中生效**。如果直接在普通函数里调用 recover(),它永远返回 nil —— 这是新手最常踩的坑。
关键点在于:panic 发生后,当前 goroutine 的栈开始展开,只有尚未执行的 defer 函数会被依次调用,recover() 就靠这个时机“截停” panic。
-
recover()必须出现在defer函数体内(不能只是 defer 一个函数名,要确保函数体里有 recover) - defer 函数必须在 panic 发生前已注册(即 panic 前就执行了 defer 语句)
- recover 只对当前 goroutine 有效,无法跨 goroutine 捕获 panic
正确写法:defer + 匿名函数 + recover
最常见且安全的模式是用 defer 包裹一个匿名函数,在其中调用 recover() 并处理返回值:
func doSomething() {
defer func() {
if r := recover(); r != nil {
log.Printf("panic recovered: %v", r)
// 这里可以返回错误、重置状态、或继续执行
}
}()
// 可能 panic 的代码
panic("something went wrong")
}
注意:recover() 返回的是 interface{},不是 error;如果需要统一错误处理,建议手动转成 error(比如用 fmt.Errorf("panic: %v", r))。
recover 失效的典型场景
以下情况 recover() 完全不起作用,程序直接崩溃:
- panic 发生在 main 函数顶层,且没加任何 defer(main 函数退出即进程终止)
- defer 写在 panic 之后(比如
panic(); defer func(){...}()),此时 defer 根本不会执行 - 在 defer 中调用了其他函数,而该函数内部又发生了 panic,但没在那个函数里 recover(recover 不会自动向上冒泡)
- 使用了
os.Exit()或向已关闭的 channel 发送数据等“不可恢复”的操作(这些不触发 defer,也不可 recover)
实际工程中怎么用才靠谱?
单纯 recover panic 往往不够,重点是“恢复后做什么”。生产环境建议:
- 只在明确知道如何兜底的地方用 recover(比如 HTTP handler、任务 worker),不要全局乱 defer
- recover 后记录完整 panic stack(用
debug.PrintStack()或runtime/debug.Stack()) - 避免在 recover 后继续执行业务逻辑(尤其涉及状态变更时),更稳妥的做法是 clean up + return error
- HTTP server 中常用
http.HandlerFunc包装器统一 recover,防止单个请求 panic 导致整个服务挂掉
recover 不是错误处理的替代品,而是最后的防线。真正该做的是提前校验、避免空指针、检查 error 返回值——panic 应该只留给真正的异常,而不是控制流。










