Go语言中defer在错误处理与recover场景下的两类疑问
Go语言中defer在错误处理与recover场景下的两类疑问
1. 为什么defer处理错误时输出nil?
先看你给出的代码:
package main import ( "errors" "fmt" ) func handleErr(err error) { fmt.Println(err) } func returnErr() (err error) { defer handleErr(err) return errors.New("boo") } func main() { returnErr() }
核心问题出在Go语言中defer语句的参数求值时机:defer后面的函数参数,是在defer声明的那一刻就计算好了,不是等到函数退出执行defer的时候才取值。
你在returnErr里写defer handleErr(err)时,err还是函数声明时的初始值nil——虽然后面return errors.New("boo")会给err赋值,但这个赋值动作是在defer参数求值之后发生的,所以handleErr拿到的始终是最初的nil。
要解决这个问题,你需要用闭包让err在defer执行时才被求值,修改后的代码如下:
func returnErr() (err error) { defer func() { handleErr(err) // 这里的err会在defer执行时取当前值,也就是返回的错误"boo" }() return errors.New("boo") }
这样运行后就能正常输出boo了。
2. 为什么defer recover()还是会触发panic?
再看你的这段代码:
package main func raisePanic() { defer recover() panic("boo") } func main() { raisePanic() }
这里的坑有两个:一是和第一个问题类似的参数求值时机,二是recover()的生效条件——recover()只有在defer包裹的函数内部调用时,才能捕获到当前goroutine的panic。
你现在写的defer recover(),是把recover()的返回值作为defer的参数,这意味着recover()在defer声明时就被调用了——这时候还没触发panic,所以recover()返回nil,完全起不到捕获panic的作用。
正确的写法是把recover放在匿名函数里,让它在defer执行时(也就是panic发生后)才被调用:
func raisePanic() { defer func() { if r := recover(); r != nil { // 这里可以自定义panic的处理逻辑,比如打印日志 println("捕获到panic:", r) } }() panic("boo") }
这样修改后,panic就会被成功捕获,程序不会崩溃了。
备注:内容来源于stack exchange,提问作者crs
相关产品推荐
相关产品推荐

