You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.16 08:18:00