引用
使用
defer是golang提供的关键字,在函数或者方法执行完成,返回之前调用。
每次defer都会将defer函数压入栈中,调用函数或者方法结束时,从栈中取出执行,所以多个defer的执行顺序是先入后出。
Defer规则
- 延迟函数的参数在defer语句出现时就已经确定下来了
- 延迟函数执行按后进先出顺序执行,即先出现的defer最后执行
- 延迟函数可能操作主函数的具名返回值
触发时机
1.包裹着defer语句的函数返回时
2.包裹着defer语句的函数执行到最后时
3.当前goroutine发生Panic时
返回值执行顺序
- 先给返回值赋值
- 执行defer语句
- 包裹函数return返回
因此,defer、return、返回值三者的执行顺序应该是:return最先给返回值赋值;接着defer开始执行一些收尾工作;最后RET指令携带返回值退出函数。
defer命令的拆解
理解这些坑的关键是这条语句:
return xxx
上面这条语句经过编译之后,变成了三条指令:
1. 返回值 = xxx
2. 调用defer函数
3. 空的return
1,3步才是Return 语句真正的命令,第2步是defer定义的语句,这里可能会操作返回值。
下面我们来看两个例子,试着将return语句和defer语句拆解到正确的顺序。
第一个例子:
func f() (r int) {
t := 5
defer func() {
t = t + 5
}()
return t
}
拆解后:
func f() (r int) {
t := 5
// 1. 赋值指令
r = t
// 2. defer被插入到赋值与返回之间执行,这个例子中返回值r没被修改过
func() {
t = t + 5
}
// 3. 空的return指令
return
}
这里第二步没有操作返回值r, 因此,main函数中调用f()得到5.
第二个例子:
func f() (r int) {
defer func(r int) {
r = r + 5
}(r)
return 1
}
拆解后:
func f() (r int) {
// 1. 赋值
r = 1
// 2. 这里改的r是之前传值传进去的r,不会改变要返回的那个r值
func(r int) {
r = r + 5
}(r)
// 3. 空的return
return
}
因此,main函数中调用f()得到1.