在 Go 里, panic 可以理解为:程序遇到了一个无法继续正常执行的严重错误,于是直接“崩溃式退出”。

它类似 PHP 里的 throw Exception,但语义更偏向于: 程序出现了不应该发生的问题 。

1. 什么是 panic?

示例: package main func main() { panic("程序出错了") }

运行结果类似: panic: 程序出错了 goroutine 1 [running]: main.main() /path/main.go:4 +0x...

意思是:程序主动触发了 panic,然后退出。

2. 常见会触发 panic 的情况

1)数组 / 切片越界 func main() { arr := []int{1, 2, 3} fmt.Println(arr[5]) }

会 panic: panic: runtime error : index out of range [5] with length 3

2)空指针调用 type User struct { Name string } func main() { var u *User fmt.Println(u.Name) }

会 panic: panic: runtime error: invalid memory address or nil pointer dereference

因为 u 是 nil ,不能访问它的字段。

3)关闭已经关闭的 channel func main() { ch := make(chan int) close(ch) close(ch) }

会 panic: panic: close of closed channel

4)往已经关闭的 channel 发送数据 func main() { ch := make(chan int) close(ch) ch <- 1 }

会 panic: panic: send on closed channel

5)类型断言失败 func main() { var x interface {} = "hello" n := x.(int) fmt.Println(n) }

会 panic: panic: interface conversion: interface {} is string, not int

所以类型断言更安全的写法是: n, ok := x.(int) if ok { fmt.Println(n) } else { fmt.Println("不是 int 类型") }

3. 主动触发 panic

可以用 panic() 主动抛出异常: func check age (age int) { if age < 0 { panic("年龄不能小于 0") } } func main() { checkAge(-1) }

这通常用于: panic("这里不应该发生")

也就是程序进入了一个理论上不该进入的状态。

4. panic 会发生什么?

当 panic 发生后: func main() { fmt.Println("开始") panic("出错了") fmt.Println("结束") }

输出: 开始 panic: 出错了

panic 后面的代码不会继续执行。

5. panic 和 defer 的关系

即使发生 panic,已经注册的 defer 仍然会执行。 func main() { defer fmt.Println("defer 执行了") fmt.Println("开始") panic("出错了") fmt.Println("结束") }

输出: 开始 defer 执行了 panic: 出错了

所以 panic 发生时,Go 会先执行当前函数中已经注册的 defer,然后再继续向上层函数传播。

6. panic 的传播过程 package main import "fmt" func a() { defer fmt.Println("a defer") panic("a 出错了") } func b() { defer fmt.Println("b defer") a() } func main() { defer fmt.Println("main defer") b() }

输出大概是: a defer b defer main defer panic: a 出错了

执行过程: main 调用 b b 调用 a a 发生 panic a 的 defer 执行 panic 传给 b b 的 defer 执行 panic 传给 main main 的 defer 执行 程序崩溃退出

7. recover:捕获 panic

Go 提供了 recover() 来恢复 panic。

注意: recover 必须在 defer 函数里面调用才有效。 package main import "fmt" func main() { defer func() { err := recover() if err != nil { fmt.Println("捕获到 panic:", err ) } }() panic("程序出错了") fmt.Println("这行不会执行") }

输出: 捕获到 panic: 程序出错了

程序不会继续崩溃。

8. recover 的经典封装 func safeRun() { defer func() { if err := recover(); err != nil { fmt.Println("捕获 panic:", err) } }() panic("发生错误") } func main() { safeRun() fmt.Println("程序继续执行") }

输出: 捕获 panic: 发生错误 程序继续执行

9. panic 和 error 的区别

Go 里大多数错误应该使用 error,而不是 panic。

error:可预期错误

比如: func findUser(id int) error { if id <= 0 { return errors.New("id 不合法") } return nil }

这种属于业务上可能发生的错误,应该返回 error。

panic:不可预期严重错误

比如: func mustInitConfig() { if config == nil { panic("配置文件加载失败") } }

这种属于程序启动时必须成功的逻辑,如果失败就没必要继续运行。

不会,除非你自己 return

可以用 recover 恢复

参数错误、查询失败、网络失败

数组越界、空指针、程序状态异常

11. Go 推荐的错误处理方式

Go 的习惯是: result, err := doSomething() if err != nil { return err }

而不是: panic(err)

也就是说: 普通错误:return error 严重 bug:panic

package main import ( "errors" "fmt" ) func divide(a, b int) (int, error) { if b == 0 { return 0, errors.New("除数不能为 0") } return a / b, nil } func main() { result, err := divide(10, 0) if err != nil { fmt.Println("错误:", err) return } fmt.Println(result) }

这里应该用 error,不应该用 panic,因为除数为 0 是一个可以预料到的输入错误。

13. 什么时候该用 panic?

一般场景: 1. 程序启动时关键配置加载失败 2. 数据库驱动初始化失败 3. 模板必须解析成功,否则程序无法启动 4. 程序进入了理论上绝不应该进入的分支 5. 测试代码中快速终止

例如: tmpl := template.Must(template.ParseFiles("index. html "))

template.Must() 内部如果发现错误,就会 panic。

相当于: tmpl, err := template.ParseFiles("index.html") if err != nil { panic(err) }

panic 是 Go 里的严重异常机制。

普通业务错误用 error: return err

程序无法继续运行时才用 panic: panic("fatal error")

而 recover 可以在 defer 中捕获 panic,避免程序直接崩溃。

本站所有文章资讯、展示的图片素材等内容均为注册用户上传(部分报媒/平媒内容转载自网络合作媒体),仅供学习参考。 用户通过本站上传、发布的任何内容的知识产权归属用户或原始著作权人所有。如有侵犯您的版权,请联系我们反馈本站将在三个工作日内改正。