panic是主动触发运行时错误以终止goroutine的机制,专用于不可恢复的严重错误或开发阶段断言失败,而非替代error的常规错误处理。

panic 不是用来“捕获”异常的,而是用来主动触发运行时错误、立即终止当前 goroutine 的执行。 Go 语言没有传统意义上的 try-catch 异常机制,也不支持“捕获 panic 并继续正常运行”的常规错误处理。真正用于错误处理的是 error 接口和显式返回值判断;而 panic 是为**不可恢复的严重错误**(如空指针解引用、切片越界、断言失败)或**开发阶段的逻辑断言失败**设计的——它本意就是中止程序,不是替代错误处理的工具。
什么时候该用 panic
仅在以下情况考虑使用:
- 函数无法继续执行且调用方无法合理处理(例如:初始化配置文件缺失、数据库连接池构建失败且无备用方案)
- 明确属于编程错误(如传入了不可能为 nil 却被假设非 nil 的参数)
- 单元测试中验证某些条件必须成立(配合 testify/assert 或原生 if !cond { panic(...) })
- 构建 DSL 或宏式代码时,用 panic 实现控制流跳转(高级用法,如 html/template 内部)
如何正确触发 panic
直接调用内置函数 panic(),参数可以是任意类型(通常用字符串或自定义 error):
示例:
func divide(a, b float64) float64 {
if b == 0 {
panic("division by zero") // 触发 panic,打印消息并终止当前 goroutine
}
return a / b
}注意:这不会“抛出后被上层 try 捕获”,而是立刻展开栈、执行 defer、然后退出当前 goroutine(若在 main goroutine,则整个程序终止)。
立即学习“go语言免费学习笔记(深入)”;
recover:仅限 defer 中拦截 panic(不推荐用于常规错误处理)
recover() 只能在 defer 函数中调用才有效,且只能捕获**同一 goroutine 中发生的 panic**。它不是为了“兜底续命”,而是用于:
- 清理资源后优雅退出(如关闭监听 socket、释放锁)
- 日志记录 panic 上下文(堆栈、输入参数)
- 在 server 主循环中防止一次 panic 导致整个服务崩溃(需谨慎设计)
示例(server 循环中的保护):
for {
conn, err := listener.Accept()
if err != nil {
log.Println("accept error:", err)
continue
}
go func(c net.Conn) {
defer func() {
if r := recover(); r != nil {
log.Printf("panic recovered from connection %v: %v", c.RemoteAddr(), r)
// 注意:此处不建议 return 或重试,通常只做日志 + 关闭连接
c.Close()
}
}()
handleConnection(c) // 若这里 panic,会被 recover 捕获
}(conn)
}别把 panic 当 error 用
常见反模式:
- 在 HTTP handler 中对用户输入校验失败就 panic —— 应返回
http.Error或 JSON 错误响应 - 数据库查询返回
sql.ErrNoRows就 panic —— 这是正常业务流,应通过 if 判断处理 - 用 panic 替代 if err != nil { return err } —— 破坏 Go 的错误显式传递哲学
Go 的哲学是:“错误是值,panic 是事故”。95% 以上的错误场景,请用 error 返回并由调用方决定如何处理。










