panic仅用于不可恢复的编程错误,recover必须在defer中调用且仅对同goroutine有效;业务错误应返回error,滥用panic会导致测试难、监控失真。

panic 不是用来处理常规错误的,recover 只在 defer 中有效且仅对同一 goroutine 生效——这两点没搞清,90% 的 recover 代码都白写了。
panic 触发后程序会立即停止当前函数执行
这不是“抛异常”意义上的中断,而是直接展开调用栈、逐层退出函数,直到遇到 recover 或者跑出 main。常见误判是以为 panic 类似 try/catch 中的 throw,可以被任意位置捕获。
- 一旦
panic发生,当前函数剩余语句(包括 return 后面的代码)全被跳过 -
defer仍会执行,但只限于当前 goroutine 中已注册、尚未执行的那些 - 如果 panic 发生在 goroutine 内部且没做 recover,整个 goroutine 会终止,但主程序可能继续运行
recover 必须写在 defer 函数里才起作用
单独写 recover() 永远返回 nil,因为只有在 panic 正在传播、且当前 defer 正被执行时,recover 才能截获它。
- 下面这段代码永远捕获不到 panic:
func bad() { panic("boom") recover() // 这行根本不会执行 } - 正确写法必须是:
func good() { defer func() { if r := recover(); r != nil { log.Println("caught:", r) } }() panic("boom") } - 注意:匿名函数里不能用 named return 变量去覆盖返回值,recover 后需显式 return
recover 无法跨 goroutine 捕获 panic
Go 的 panic/recover 是 goroutine 局部机制,go func() { panic("x") }() 中的 panic 永远无法被外层 recover 拦截。
立即学习“go语言免费学习笔记(深入)”;
- 典型翻车现场:HTTP handler 里起 goroutine 处理耗时任务,里面 panic 了,handler 却毫无感知
- 解决方案不是加 recover,而是统一用
errgroup.Group或封装带 recover 的 worker: go func() { defer func() { if r := recover(); r != nil { log.Printf("worker panic: %v", r) } }() doWork() }()- 别试图在主 goroutine 里 recover 子 goroutine 的 panic —— 语言层面就不支持
什么时候该用 panic,什么时候该用 error 返回
标准库里 panic 只用于真正不可恢复的编程错误,比如 nil 指针解引用、数组越界、断言失败;业务逻辑错误(如参数校验失败、文件不存在)一律走 error 返回。
- HTTP 路由找不到 handler?→
return nil, fmt.Errorf("not found") - 调用
json.Unmarshal解析非法 JSON?→ 它本身返回error,你接着传出去就行 - 自己写的工具函数里 assert(len(s) > 0),结果 s 是空切片?→ 这才是 panic 的合理场景
- 滥用 panic 会导致测试难写、监控失真、错误堆栈污染真实问题点
真正麻烦的是 panic 发生在第三方库内部又没暴露错误通道,这时候你只能靠 defer+recover 做兜底,但得清楚:这不是修复问题,只是避免进程挂掉。这种 case 往往意味着该换库,或者给上游提 issue。










