recover必须写在defer匿名函数中,且需在可能panic的转换代码前注册;跨goroutine panic无法被主线程recover捕获;recover后应立即返回,不可继续使用可能已损坏的数据状态。

recover 必须写在 defer 匿名函数里,不能直接调用
数据转换场景(比如 json.Unmarshal、strconv.Atoi、自定义结构体映射)本身不会触发 panic,但一旦混入越界访问、空指针解引用、并发 map 写入等操作,就可能在转换途中崩掉。这时候想“兜住”崩溃,recover() 放错位置等于没写。
常见翻车写法:recover() 写在 if 判断里、放在转换逻辑之后、或者直接写成 defer recover()——这会在注册时立刻执行,返回 nil,后续 panic 完全捕获不到。
- 必须用
defer func() { if r := recover(); r != nil { /* 处理 */ } }()这种闭包形式 - 这个
defer要在任何可能 panic 的转换代码之前注册(语法顺序上“先 defer,后转换”) - 如果转换函数本身是封装好的(如
ParseUserInput()),defer得包在它的调用方函数里,不能指望它内部自己 recover
跨 goroutine 的 panic 无法被主线程的 recover 捕获
数据转换常伴随并发:比如开多个 goroutine 并行解析 JSON 字段、或用 sync.Map 缓存转换结果。这时子协程里 panic("invalid type"),主线程即使写了 defer+recover 也完全无感,进程照样退出。
根本原因:每个 goroutine 的 panic 是隔离的,recover() 没有跨协程传播能力。
- 每个可能 panic 的 goroutine 都得自己配一套
defer func() { recover() }() - HTTP handler 中做数据转换,就得在每个 handler 函数开头加 defer;worker 协程同理
- 别幻想“全局中间件式 recover”,Go 没这机制
recover 后不能继续使用已破坏的数据状态
数据转换中常见的 panic 来源:切片越界(data[100])、map 并发读写、解引用 nil 指针(user.Name 但 user == nil)。这些操作会破坏内存或运行时结构,recover() 只能停止 panic 传播,但不修复状态。
例如:在 json.Unmarshal 前手动做了 slice = append(slice[:0], ...),但 slice 底层数组已被并发写坏,recover 成功后继续用这个 slice,大概率二次 panic。
- recover 后应立即返回或退出当前函数,不要尝试“继续转换”
- 资源清理(如关闭临时文件、释放锁)要放在独立的 defer 链里,而不是 recover 分支中
- 避免在 recover 分支里调用
close(ch)、向 map 写入、或访问刚 panic 过的字段
recover 返回值类型不确定,必须先判空再断言
数据转换中触发的 panic 类型五花八门:panic("bad format") 返回 string,panic(errors.New("io timeout")) 返回 error,panic(runtime.Error)(如 index out of range)返回的是 runtime.Error 接口类型。直接 r.(error).Error() 会再次 panic。
正确做法永远是两步:先检查是否非空,再按需类型处理。
- 用
if r := recover(); r != nil { ... }开头,绝不能省略r != nil - 日志记录推荐统一转成字符串:
fmt.Sprint(r),比强制断言安全 - 若需区分错误类型,用
switch r.(type),而不是嵌套r.(error)
最易被忽略的一点:recover 不是重试机制,也不是错误降级开关。它只负责让当前 goroutine 软着陆。数据转换出错,真正该做的是返回明确的 error,由上层决定重试、降级或告警——靠 recover “吞掉 panic 继续跑”只会掩盖问题,让状态更不可控。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











