defer的一些坑
核心点
核心1: defer是在return之前执行的。
核心2: return本身不是一条原子操作。
坑的例子
package main
import "fmt"
func main() {
fmt.Println(keng1())
fmt.Println(keng2())
fmt.Println(keng3())
fmt.Println(keng4())
}
/**
return 0, 不是原子操作,先赋值后返回
其中赋值与返回之间执行defer内容, 这就是官方资料中说的所谓的在return之前defer
*/
// output 1
func keng1() (ret int) {
defer func() {
ret++
}()
return 0
}
// output 5
func keng2() (ret int) {
t := 5
defer func() {
t = t + 5
}()
return t
}
// output 1
func keng3() (r int) {
defer func(r int) {
r += 5
}(r)
return 1
}
/**
不带命名参数
keng4的情况其实是和keng2一样的, keng4无返回命名,但是返回值的地址空间还是有的
执行步骤如下
ret := 0
result = ret
defer func() { ret++ }()
return result
*/
// output 0
func keng4() int {
ret := 0
defer func() {
ret++
}()
return ret
}
这边有四个坑,无论是哪种坑,只要把defer和return拿出来,做上面核心所说的分析就行了。
如keng1函数
// output 1
func keng1() (ret int) {
defer func() {
ret++
}()
return 0
}
其实可以翻译成
ret = 0
ret++
return // 这部分return的是ret, so output is 1
如keng2函数
// output 5
func keng2() (ret int) {
t := 5
defer func() {
t = t + 5
}()
return t
}
其实可以翻译成
t := 5
// 以下开始是return拆分,并参入了defer的内容
ret = t
t = t + 5
return // 这部分return的是ret, so output is 5
keng3函数自行分析,直接跳到keng4函数,为什么说它和keng2是一样的呢?
// output 0
func keng4() int {
ret := 0
defer func() {
ret++
}()
return ret
}
没有用命名的返回值,但是我们可以认为存在(事实上地址空间还是开辟着的),所以暂且用result表示这块返回区域
ret := 0
// you know
result = ret
ret++
return // return 的是result, so output is 0
从翻译内容来看,就是keng2的内容。
2020-07-29 补充
每次defer语句执行的时候,会把函数“压栈”,函数参数会被拷贝下来;当外层函数(非代码块,如一个for循环)退出时,defer函数按照定义的逆序执行;如果defer执行的函数为nil, 那么会在最终调用函数的产生panic.