recover必须在defer中调用才有效,仅在panic发生且当前goroutine的defer链执行时返回panic值,否则返回nil;它不回滚副作用,仅实现goroutine软着陆。

recover 必须在 defer 中调用才有效
直接在普通函数体里写 recover() 永远返回 nil,它只在 panic 正在发生、且当前 goroutine 的 defer 链正在执行时才有意义。Go 运行时会把 panic 的值“传递”给正在执行的 defer 函数中的 recover() 调用。
- 必须搭配
defer使用,典型模式是:defer func() { if r := recover(); r != nil { // 处理 panic 值 log.Printf("panic recovered: %v", r) } }() - 如果 defer 是匿名函数,且该函数内没调用
recover(),或者调用位置不在 panic 触发之后(比如提前 return),就捕获不到 - recover 不是“全局开关”,它只对**当前 goroutine** 有效;其他 goroutine 的 panic 不会影响它,也不能被它捕获
recover 返回值类型是 interface{},需手动断言
recover() 返回的是任意类型的 panic 值,不是错误对象。如果你用 panic("oops"),recover 得到的是 string;如果用 panic(errors.New("db fail")),得到的是 *errors.errorString;甚至可能是自定义结构体。
- 不要假设返回值一定是
error类型,直接转成error可能 panic:if err := recover().(error); err != nil { ... } // 危险!类型断言失败会再 panic - 安全做法是先做类型检查:
r := 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 无法恢复已释放的资源或已提交的状态
recover 只是让 goroutine 从 panic 状态中“软着陆”,继续执行 defer 后的代码,但它不撤销任何已发生的副作用。
- 文件已写入、数据库事务已提交、HTTP 响应头已发送……这些都不会回滚
- recover 后继续执行的逻辑,仍要面对一个可能处于不一致状态的对象(比如 struct 字段部分初始化失败)
- 常见误用:在 HTTP handler 里 recover 后直接返回 200 OK,但其实业务逻辑已经中断,响应内容为空或错乱
- 正确思路是:recover → 记录上下文(如 request ID、panic 栈)→ 返回 500 或降级响应 → 避免暴露内部细节
嵌套 defer 和多层 recover 的行为容易误判
一个函数里可以有多个 defer,它们按后进先出(LIFO)顺序执行;每个 defer 里的 recover() 都有机会捕获 panic,但只有第一个成功调用的能拿到 panic 值,后续都返回 nil。
立即学习“go语言免费学习笔记(深入)”;
- 下面这段代码只会打印一次 "recovered":
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 只作用于当前函数的 defer 链;父函数即使有 defer + recover,也捕获不到子函数里已被子函数自身 recover 掉的 panic
- 在中间件或 wrapper 函数中统一 recover 是可行的,但要注意别和业务层的 recover 冲突 —— 建议只在最外层(如 HTTP handler、goroutine 入口)做一次 recover
recover 不是 try/catch,它没有“异常传播”概念,也没有 finally 语义。真正难的从来不是写那行 recover(),而是判断哪里该用、哪里不该用,以及 panic 之后你还敢信任哪些数据。










