recover仅在panic传播中且未退出当前goroutine时有效,必须在defer匿名函数内直接调用,仅对同goroutine panic生效,不可跨协程或跨函数捕获,recover后不应继续业务逻辑。

recover 只在特定条件下有效:它不是“捕获 panic 的函数”,而是“在 panic 正在传播、且尚未退出当前 goroutine 时,提供一次中断传播并取回 panic 值的机会”。绝大多数写法都返回 nil,因为不满足前提。
recover 必须在 defer 匿名函数体内直接调用
这是最硬的语法约束。任何间接调用都会失效:recover 不能赋值给变量、不能包在另一个函数里、不能放在 goroutine 中调用。
-
defer func() { recover() }()✅ 合法,recover()是 defer 函数体内的直接语句 -
defer func() { r := recover(); r() }()❌ 无效,recover被当成值赋给r,再调用r()会报错(cannot call non-function r) -
defer recover()❌ 语法错误,recover不是可调用表达式,不能作为 defer 的参数 -
go func() { recover() }()❌ 在新 goroutine 中调用,永远返回nil
defer 必须在 panic 发生前注册
顺序错了就彻底失效。panic 触发后立即展开栈,只执行**已注册**的 defer;后注册的 defer 根本没机会运行。
- ✅ 正确:
defer func() { recover() }(); panic("x")→ defer 先注册,panic 后触发 - ❌ 错误:
panic("x"); defer func() { recover() }()→ panic 已终止函数,defer 永远不会执行 - ⚠️ 注意:多个 defer 按后进先出执行,如果写了两个 defer,
recover()必须放在更靠近 panic 的那个里,否则可能被前面的 defer “提前消耗”返回值
recover 只对同 goroutine 的 panic 有效
Go 的 panic/recover 是 goroutine 局部机制,不存在跨协程捕获这回事。主线程加了 recover,对 go func() { panic("x") }() 完全无感。
- HTTP handler 里起 goroutine 处理耗时任务?必须在该 goroutine 内部自己加
defer func() { recover() }() - 封装
safeGo工具函数时,要确保传入函数体内部已带 recover,否则只是把崩溃从显式变成静默 - 子 goroutine panic 后只会自己退出,主 goroutine 不会收到通知,也不会触发其 defer —— 这是设计,不是 bug
recover 后不能继续业务逻辑
它不是重试或回滚机制,只是提供一次“软着陆”机会。panic 发生点之后的局部状态大概率已损坏,继续执行极易引发二次 panic 或数据不一致。
- 切片越界后,
arr[100] = 1失败,但arr可能部分初始化、指针可能已解引用失败 - recover 分支里不要调用依赖该状态的代码(如再次写 DB、发 HTTP 请求)
- 合理做法:记录堆栈 + request ID → 返回 500 或 fallback 响应 → 显式
return或os.Exit(1) -
recover()返回的是interface{},务必先判断r != nil,再用fmt.Sprint(r)输出,避免类型断言失败导致二次 panic
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











