Go 怎么实现类似 try-catch 的 panic 恢复?

6次阅读

go 中 recover 必须在 defer 函数体内调用才有效,直接在普通函数中调用始终返回 nil;正确写法是 defer 匿名函数并在其中调用 recover 处理 panic。

Go 怎么实现类似 try-catch 的 panic 恢复?

Go 里没有 try-catch,recover 必须配合 defer 使用

Go 不支持传统 try-catch 语法,recover 是唯一能“捕获” panic 的机制,但它**只能在 defer 函数中生效**。如果直接在普通函数里调用 recover(),它永远返回 nil —— 这是新手最常踩的坑。

关键点在于:panic 发生后,当前 goroutine 的开始展开,只有尚未执行的 defer 函数会被依次调用,recover() 就靠这个时机“截停” panic。

  • recover() 必须出现在 defer 函数体内(不能只是 defer 一个函数名,要确保函数体里有 recover)
  • defer 函数必须在 panic 发生前已注册(即 panic 前就执行了 defer 语句)
  • recover 只对当前 goroutine 有效,无法跨 goroutine 捕获 panic

正确写法:defer + 匿名函数 + recover

最常见且安全的模式是用 defer 包裹一个匿名函数,在其中调用 recover() 并处理返回值:

func doSomething() {     defer func() {         if r := recover(); r != nil {             log.Printf("panic recovered: %v", r)             // 这里可以返回错误、重置状态、或继续执行         }     }()     // 可能 panic 的代码     panic("something went wrong") }

注意:recover() 返回的是 Interface{},不是 Error;如果需要统一错误处理,建议手动转成 error(比如用 fmt.Errorf("panic: %v", r))。

recover 失效的典型场景

以下情况 recover() 完全不起作用,程序直接崩溃:

  • panic 发生在 main 函数顶层,且没加任何 defer(main 函数退出即进程终止)
  • defer 写在 panic 之后(比如 panic(); defer func(){...}()),此时 defer 根本不会执行
  • 在 defer 中调用了其他函数,而该函数内部又发生了 panic,但没在那个函数里 recover(recover 不会自动向上冒泡)
  • 使用了 os.Exit() 或向已关闭的 channel 发送数据等“不可恢复”的操作(这些不触发 defer,也不可 recover)

实际工程中怎么用才靠谱?

单纯 recover panic 往往不够,重点是“恢复后做什么”。生产环境建议:

  • 只在明确知道如何兜底的地方用 recover(比如 http handler、任务 worker),不要全局乱 defer
  • recover 后记录完整 panic stack(用 debug.PrintStack()runtime/debug.Stack()
  • 避免在 recover 后继续执行业务逻辑(尤其涉及状态变更时),更稳妥的做法是 clean up + return error
  • HTTP server 中常用 http.HandlerFunc 包装器统一 recover,防止单个请求 panic 导致整个服务挂掉

recover 不是错误处理的替代品,而是最后的防线。真正该做的是提前校验、避免空指针、检查 error 返回值——panic 应该只留给真正的异常,而不是控制流。

text=ZqhQzanResources