defer 的一些用法和猜测

以前的代码中,基本上只使用了 defer 作为防御程序 panic 退出的手段,没有仔细考虑过对返回值的影响。今天有同事提到:

如果执行过程中发生 panic,defer函数 recover() != nil
的情况下,未命名的返回值的函数会返回什么呢?
之前并没有没有想过这个问题,猜想应该是返回该类型的默认值,试了一下果然如此。

func fooA() float64 {
    defer func() {
        if err := recover(); err != nil {
        }
    }()

    *(*float64)(nil) = 0.1 // panic
    return 1.0
}

fooA()
返回0。
那么如果 defer 函数返回值和上层函数一致,会不会替换掉返回值呢?(


会就有鬼了


)

func fooB() float64 {
    defer func() float64 {
        if err := recover(); err != nil {
            return 1.1
        } else {
            return 1.2
        }
    }()

    *(*float64)(nil) = 0.1
    return 1.0
}

fooB()
返回0。
事实上,如果要在 defer 中修改函数的返回值,目前我只知道一种办法:使用命名的返回参数:

func fooC() (x float64) {
    defer func() {
        if err := recover(); err != nil {
            x = 1.1
            return
        }
    }()

    *(*float64)(nil) = 0.1
    return 1.0
}

fooC()
返回1.1。

官方文档上面的说明:
https://golang.org/ref/spec#Defer_statements

核心的说明我认为是这一句话:

That is, if the surrounding function returns through an explicit return statement
, deferred functions are executed  after
any result parameters are set by that return statement but  before
the function returns to its caller.

搜索信息的过程中发现这样的一篇文章: 一道考察defer与命名返回值的题目

DeferFunc1 和 DeferFunc3 还比较好解释,对于文中的DeferFunc2,强行猜测了一下:

func dfB(i int) int { // return anonymous int,we assume it named `noNameI`。
    t := i // Local var t is assigned to 1.
    defer func() {
        // Here noNameI is 2, t is 1.
        t += i // t is 3, noNameI unchanged.
    }()
    return 2 // noNameI is assigned to 2.
}

dfB()
返回 2,为什么呢?

代码 return 2
的时候应该是将2赋值给了匿名的返回值变量,因此 defer 函数中的对本地变量的 t 的操作便无关紧要。