recover必须在defer函数中直接调用才有效,仅捕获同goroutine内由panic()主动触发的异常,对运行时错误(如空指针、越界)无效,不可替代显式error处理。

recover 必须在 defer 中调用才有效
Go 没有传统 try/catch,recover 只能在 defer 函数中起作用,且仅对当前 goroutine 的 panic 生效。如果在普通函数里直接调用 recover(),它永远返回 nil,不会捕获任何东西。
- 必须写成
defer func() { if r := recover(); r != nil { /* 处理 */ } }()形式 - 不能写成
defer recover()—— 这会立即执行recover,此时还没 panic,返回nil - 如果 panic 发生在其他 goroutine,当前 goroutine 的
defer里的recover完全无感知
recover 只能捕获 panic,不能捕获 runtime error
recover 只拦截由 panic() 主动触发的异常,对 Go 运行时抛出的致命错误(如空指针解引用、切片越界、除零)无效——这些会导致程序直接崩溃,defer 甚至都来不及执行。
- 例如
var p *int; fmt.Println(*p)触发panic: runtime error: invalid memory address...,recover捕不到 - 但
panic("custom error")或panic(fmt.Errorf("oops"))可被正常捕获 - 想拦截运行时错误?做不到。只能靠静态检查、单元测试、pprof 分析或外部监控
recover 后程序继续执行,但栈已展开完毕
成功调用 recover 后,panic 被终止,控制权回到 defer 所在函数的末尾,后续代码照常运行。但要注意:panic 发生点之前的调用栈已经全部展开(所有中间函数的 defer 已执行),无法“回滚”或“重试”那部分逻辑。
- 常见误用:在
recover后试图继续使用已失效的局部资源(比如 panic 前打开的文件句柄可能已关闭) - 不要依赖 panic 点附近的变量状态;应把关键数据提前保存到
defer闭包外的作用域 - 示例:
func risky() { f, _ := os.Open("x") defer func() { if r := recover(); r != nil { log.Printf("recovered: %v", r) f.Close() // ✅ 安全:f 在闭包外可见 } }() panic("boom") }
不要用 recover 替代错误处理
Go 鼓励显式错误返回(if err != nil),recover 是兜底机制,只该用于防止整个程序崩溃的边界场景,比如 HTTP handler 中避免一个请求 panic 导致服务退出。
- 在业务逻辑里频繁用
panic/recover会让控制流难以追踪,也违背 Go 的错误哲学 - HTTP server 示例中,通常只在顶层 handler 包一层
defer/recover,而不是每个子函数都加 - recover 返回值类型是
interface{},需要类型断言才能取真实错误,别忘了判空和类型检查
实际中最容易忽略的是:recover 对运行时崩溃完全无效,而开发者常误以为它能“catch all”。真遇到空指针或越界,程序早停了,连 defer 都没机会跑。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











