Go 中的 panic 是程序崩溃前的最后警告信号,非异常处理工具;nil 指针解引用、并发写 map、越界访问、向已关闭 channel 发送数据会触发不可恢复 panic。
Go 中的 panic 不是“异常捕获工具”,而是程序崩溃前的最后警告信号;它不该被用来处理业务错误,也不能替代 error 返回值。
这些不是开发者主动写的 panic,而是 Go 运行时检测到非法状态后强制中止程序——它们无法被 recover 拦截(或极难可靠拦截),必须靠预防:
nil 指针解引用:var p *int fmt.Println(*p) // panic: invalid memory address or nil pointer dereference
map:var m = map[string]int{}
go func() { m["a"] = 1 }()
go func() { m["b"] = 2 }() // panic: concurrent map writes(此 panic 通常无法 recover)s := "hi" fmt.Println(s[5]) // panic: string index out of range
chan 发送数据:ch := make(chan int, 1) close(ch) ch <- 42 // panic: send on closed channel
var i interface{} = 42
s := i.(string) // panic: interface conversion: int is not string仅限于「程序已失去继续运行意义」的场景,且应发生在启动期或内部逻辑断言环节。一旦进入业务处

error。
func init() {
cfg, err := loadConfig("config.yaml")
if err != nil {
panic(fmt.Sprintf("init failed: %v", err)) // ✅ 合理:main 不会执行
}
}func getUserName(u *User) string {
if u == nil {
panic("getUserName: u must not be nil") // ✅ 合理:调用方传了非法参数
}
return u.Name
}func calcRate(total, count int) float64 {
if count == 0 {
panic("calcRate: count must not be zero") // ⚠️ 仅用于 debug/assert,不用于用户输入校验
}
return float64(total) / float64(count)
}recover 只在 defer 函数中有效,且只能捕获当前 goroutine 的 panic。它不是“兜底容错”,而是一种有限的、需谨慎设计的恢复手段。
func main() {
defer func() {
if r := recover(); r != nil {
log.Printf("PANIC in main: %+v", r)
// 执行清理(如关闭监听、flush 日志)
os.Exit(1)
}
}()
http.ListenAndServe(":8080", nil)
}go func() {
panic("from goroutine") // 这个 panic 不会被 main 中的 recover 捕获
}()func handleRequest(r *http.Request) {
defer func() {
if r := recover(); r != nil {
// ❌ 错误:掩盖真实问题,且可能让状态不一致
}
}()
// ... 处理逻辑
}真正危险的不是 panic 本身,而是对它的误判和误用:
panic 当成 try/catch —— Go 没有这种抽象,error 才是第一公民;recover 吞掉 panic 并返回 200 —— 这会让调用方误以为成功,掩盖严重 bug;recover 来修复并发 map 写冲突 —— 此类 panic 往往伴随内存损坏,recover 后行为不可预测;mutex.Unlock()),反而引发二次 panic,导致原始错误丢失。