引入
程序有时会碰到完全无法处理的情况——数组越界、空指针、除零。Go 里这类严重问题会触发 panic,程序会立刻停止正常运行。但 Go 也提供了 recover 机制,让你在 defer 里把程序从崩溃边缘拉回来。
正文
定义
定义
panic是 Go 的运行时错误机制,调用panic()后程序立即停止正常执行,开始执行所有defer语句,然后崩溃。recover()只能在defer函数内调用,用于捕获panic并恢复程序的正常执行。
语法
// 触发 panic
panic("出错了")
// 在 defer 中恢复
defer func() {
if r := recover(); r != nil {
fmt.Println("恢复了:", r)
}
}()例子
触发 panic
func main() {
fmt.Println("开始")
panic("严重错误")
fmt.Println("不会执行")
}
// 输出:
// 开始
// panic: 严重错误panic() 后面的代码不会再执行,程序打印 panic 信息和调用栈后退出。
panic 时 defer 仍然执行
func main() {
defer fmt.Println("defer 执行了")
panic("崩溃了")
}
// 输出:
// defer 执行了
// panic: 崩溃了即使 panic 了,之前声明的 defer 照样会执行,按 LIFO 顺序逐个运行。
用 recover 恢复
func safeDivide(a, b int) (result int) {
defer func() {
if r := recover(); r != nil {
fmt.Println("捕获 panic:", r)
result = 0
}
}()
return a / b
}
func main() {
fmt.Println(safeDivide(10, 2)) // 5
fmt.Println(safeDivide(10, 0)) // 捕获 panic: runtime error: ...
// 0
}recover() 在 defer 里调用,如果当前函数正在 panic,就能拿到 panic 的值并恢复正常执行。
recover 必须在 defer 里
func main() {
// 错误写法:直接调用 recover 无效
r := recover()
fmt.Println(r) // nil
panic("test")
}recover() 只有在 defer 函数中调用才有效,直接调用永远返回 nil。
recover 只恢复当前 goroutine
func main() {
defer func() {
recover() // 无法恢复子 goroutine 的 panic
}()
go func() {
panic("子 goroutine 崩溃")
}()
time.Sleep(time.Second)
}recover() 只能恢复当前 goroutine 的 panic,其他 goroutine 的 panic 无法跨 goroutine 捕获。
常见 panic 场景
// 数组越界
arr := [3]int{1, 2, 3}
_ = arr[5] // panic: index out of range
// 空指针
var p *int
_ = *p // panic: nil pointer dereference
// 向 nil map 写入
var m map[string]int
m["key"] = 1 // panic: assignment to entry in nil map
// 重复关闭 channel
ch := make(chan int)
close(ch)
close(ch) // panic: close of closed channel这些是 Go 运行时自动触发的 panic,不需要手动调用 panic()。
判断是否 panic
func handle() {
defer func() {
if r := recover(); r != nil {
fmt.Printf("panic 类型: %T, 值: %v\n", r, r)
} else {
fmt.Println("正常结束")
}
}()
// 正常或 panic 的代码
}recover() 返回 interface{} 类型,可以用 %T 查看 panic 值的具体类型。
常见写法
| 写法 | 说明 |
|---|---|
panic("msg") | 手动触发 panic |
recover() | 在 defer 中捕获 panic |
if r := recover(); r != nil { } | 判断并处理 panic |
defer func() { recover() }() | 简洁的 panic 恢复 |
panic(errors.New("err")) | panic 传 error 值 |
特点
panic会让程序立即停止正常执行,所有defer执行完后崩溃recover()只能在defer里生效,直接调用无效recover()只能恢复当前 goroutine 的 panic- Go 不鼓励用
panic/recover替代正常错误处理,日常用error panic适合真正无法恢复的情况,比如初始化失败、程序逻辑错误- 运行时错误(越界、空指针等)会自动触发 panic
panic的值是interface{}类型,可以是任意值
理解
panic 是 Go 的”紧急按钮”,程序遇到无法处理的问题时拉响警报。recover 则是安全网,在 defer 里把程序从崩溃中救回来。但 Go 的设计哲学是用 error 处理可预见的错误,panic 只留给真正的意外情况。别把 panic/recover 当 try-catch 用。
引出
panic 和 recover 是 Go 控制流的最后一环。接下来进入数据类型的学习,从最基础的 Go 数组 开始,看看 Go 里怎么存储一组固定长度的数据。