error
go语言和其他主流语言比较,被谈及最多的内容可能就是繁琐的错误处理
刚开始的时候觉得每次都要接收错误很麻烦,写多了习惯了就会感觉还行,可以快速的定位到错误的地方
大量的使用函数和方法返回 error,使得调用代码变得特别难看,也不优雅,一大堆的检查语句充实代码段间
可用的几种解决方法
1、使用定制的检查函数处理错误逻辑,简化检查,比如记录日志
2、在不影响逻辑的情况下,使用 defer 延后处理错误状态,err 退化赋值
3、在不中断逻辑的情况下,将错误作为内部状态保存,等最后面提交时再处理错误
panic、recover
与 error相比,panic/recover
使用方法上更接近 try/catch
panic
会立即中断当前函数流程,执行延时调用
而在延时函数中,recover
可捕获并返回 panic提交的错误对象
func main() {
defer func() {
if err := recover(); err != nil {
log.Fatalln(err)
}
}()
panic("触发异常") // 引发异常
println("退出。。。。") // 永远不会执行
}
// 2022/09/23 16:01:11 触发异常
所有的延时调用不管如何,都会被执行,但中断错误会沿着调用堆栈向外传递
要么是被外层捕获,要么就是触发进程崩溃
func test() {
defer println("test 1")
defer println("test 2")
panic("触发异常")
}
func main() {
defer func() {
if err := recover(); err != nil {
log.Fatalln(err)
}
}()
test()
}
// test 2
// test 1
// 2022/09/23 16:10:14 触发异常
还有当连续调用 panic时,又会被如何 recover捕获呢
func main() {
defer func() {
for {
if err := recover(); err != nil {
log.Fatalln(err)
} else {
log.Fatalln("未捕获到异常")
}
}
}()
defer func() {
panic("再次触发异常")
}()
panic("触发异常")
}
// 2022/09/23 16:15:58 再次触发异常
// 只有最后一个会被 recover捕获
特别需要说一下的,recover必须在延时函数中执行才能生效
func main() {
defer func() {
log.Fatalln("test 1:", recover())
}()
defer println("test 2:", recover())
defer recover()
panic("触发异常")
}
// test 2: (0x0,0x0)
// 2022/09/23 16:23:06 test 1: 触发异常
// 可以看见只有延时函数中的 recover被生效
因为 recover这个特性,那么如果要保护某个代码片段,那么只能将其重构为函数再调用
func test(x, y int) interface{} {
z := 0
func() {
defer func() {
if err := recover(); err != nil {
log.Println(err)
}
}()
z = x / y
}()
return z
}
func main() {
result := test(1, 0)
fmt.Print("1 / 0 = ",result)
}
// 2022/09/23 16:38:31 runtime error: integer divide by zero
// 1 / 0 = 0
// 对 x / y 做了保护
写在最后
当然了,还是建议除非是不可恢复性、导致系统无法正常工作的错误,否则不建议使用 panic
比如:端口服务被占用、数据库连接失败、文件系统没有权限