recover必须在defer函数中调用才有效,仅捕获当前goroutine正在传播的panic;跨goroutine无效,且recover后不可继续使用可能已损坏的状态。

panic 和 recover 不是 Go 的错误处理主力,而是兜底手段;用错位置、时机或方式,recover 就等于没写。
recover 必须在 defer 函数里调用才有效
这是最常踩的坑:把 recover() 写在普通代码流中,或者放在非 defer 的闭包里,它永远返回 nil,什么也捕获不到。
-
recover()只对“当前 goroutine 正在传播、且尚未退出函数”的 panic 生效 - 如果 panic 已经传到上层函数、或者函数已 return,
recover()就失效了 - 常见错误写法:
func f() { recover(); panic("x") }—— 这里recover()根本不触发 - 正确写法必须是:
defer func() { if r := recover(); r != nil { ... } }()
panic 会传播,recover 只能捕获本 goroutine 的 panic
主线程里写个 defer recover(),对子 goroutine 里的 panic 完全无效。这不是 bug,是 Go 并发模型的设计前提。
- 启动新 goroutine 时,它的 panic 是独立的,不会跨 goroutine 传播
- HTTP handler 中想防止单个请求 panic 导致整个服务挂掉?必须在 handler 函数内部加
defer recover() - 全局错误兜底?不存在。只能在每个可能 panic 的 goroutine 入口处手动防护
- 比如:
go func() { defer func(){ recover() }(); riskyCode() }()
recover 后不能继续用已损坏的状态
recover 让程序“继续往下跑”,但不代表状态是安全的。越界访问数组后 recover,那个 slice 可能已经处于未定义行为边缘。
- 不要在
recover()后继续读写已 panic 的资源(如已 close 的 channel、已释放的内存、已破坏的 struct 字段) - recover 后应尽快清理、记录、返回,而不是试图“修复”并继续逻辑
- 常见误操作:recover 后仍调用
close(ch)或继续向已 panic 的 map 写入 —— 可能二次 panic - 建议模式:
if r := recover(); r != nil { log.Printf("panic: %v", r); return }
panic 值类型是 interface{},不是 error
直接用 fmt.Errorf("%w", r) 会编译失败;不显式转字符串,日志里可能只看到 <nil></nil> 或空值。
-
recover()返回的是interface{},可能是 string、error、自定义 struct 等 - 安全打印方式:
fmt.Sprint(r)或fmt.Sprintf("%v", r) - 若需结构化处理,先做类型断言:
if err, ok := r.(error); ok { ... } - 忽略
r != nil判断,直接log.Print(recover())—— 日志里永远是<nil></nil>
真正难的不是写对那三行 defer func(){...}(),而是在 panic 发生后,判断哪些变量还可信、哪些资源该立刻丢弃、哪些状态必须重置——这些没法靠语法检查,只能靠对代码路径和数据生命周期的理解。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











