defer-的一些用法和猜测

30次阅读

共计 1202 个字符,预计需要花费 4 分钟才能阅读完成。

以前的代码中,基本上只使用了 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 的操作便无关紧要。

正文完
 0