在Go语言里,panic用于终止正常流程并抛出运行时异常。它既可以由代码主动调用,也可能因运行时错误自动触发。理解它的使用方式和风险,对编写健壮的Go程序非常重要。

如何主动使用panic
在函数中直接调用内置的panic()函数即可触发。它接收一个interface{}类型的参数,通常传字符串说明原因。
package main
import "fmt"
func divide(a, b int) int {
if b == 0 {
panic("division by zero")
}
return a / b
}
func main() {
fmt.Println(divide(10, 0))
}
运行上面代码会立即终止并打印panic信息及调用栈。
panic的常见触发条件
除了主动调用,以下情况会由运行时自动触发panic:
- 访问数组或切片越界,如arr[10]但长度只有5
- 向空指针或nil接口调用方法
- 关闭已关闭的channel
- 并发写map且未加锁
- 类型断言失败且使用单返回值形式
panic的风险说明
滥用panic会带来明显问题。首先,未捕获的panic会导致整个程序退出,在服务器程序中可能中断所有请求。其次,panic发生后若没有defer释放资源,会造成文件句柄或连接泄漏。此外,用panic控制正常业务错误会让调用方难以预期。
Go官方建议:panic应只用于真正异常的、不可恢复的错误,普通错误请用error返回。
使用recover捕获panic
可以在defer函数中调用recover()来截获panic,使程序恢复执行。
package main
import "fmt"
func safeRun() {
defer func() {
if r := recover(); r != nil {
fmt.Println("recovered:", r)
}
}()
panic("something wrong")
}
func main() {
safeRun()
fmt.Println("program continues")
}
recover使用限制
recover只有在defer直接调用的函数内才有效,若在嵌套函数中调用会返回nil。
panic与error的选择
| 场景 | 推荐方式 |
|---|---|
| 配置文件缺失 | 返回error |
| 数组越界(逻辑bug) | 主动panic或修复代码 |
| 网络请求失败 | 返回error |
| 初始化严重依赖不存在 | panic |
在库代码中应尽量避免panic,由调用者决定如何处理错误,这样更灵活也更安全。