「这是我参与2022首次更文挑战的第16天,活动详情查看:2022首次更文挑战」。
前言
大多数人使用defer都是用于数据库,文件描述符的关闭,资源的解锁。像context一样,在项目中看到有他们的出现,却对其认识知之甚少。
本文主要通过几个defer返回值的例子介绍defer和return的顺序。
举个例子
func TestDefer(t *testing.T){
i := f()
fmt.Printf("i = %d ,g = %d\n", i, g)
}
var g = 100
func f() (ret int) {
defer func() {
g = 200
}()
fmt.Printf("f: g = %d\n", g)
return g
}
输出:
=== RUN TestDefer
f: g = 100
i = 100 ,g = 200
--- PASS: TestDefer (0.00s)
PASS
func TestDefer(t *testing.T){
i := f()
fmt.Printf("i = %d ,g = %d\n", i, g)
}
var g = 100
func f() (ret int) {
ret = g
defer func() {
ret = 200
}()
ret = 0
return g
}
输出:
=== RUN TestDefer
i = 200 ,g = 100
--- PASS: TestDefer (0.00s)
PASS
两者的结果比较很容易让人误解,因为前者看起来是先执行return后执行defer,第二个例子则看起来像是先执行defer,后执行return。
问题的关键在于defer其实是在return中间执行。
我们知道,当调用一个函数A时,在函数A执行完后,A会将调用者所需返回值存储在栈上,然后在返回。
defer执行时刻正是“A将调用者所需返回值存储在栈上”后,函数A返回之前.
再举个例子
func f() (ret int) {
defer func(r int) {
r = 3
}(ret)
return 1
}
输出:
=== RUN TestDefer i = 1 --- PASS: TestDefer (0.00s)
func f() (ret int) {
defer func(r int) {
ret = 3
}(ret)
return 1
}
输出:
=== RUN TestDefer i = 3 --- PASS: TestDefer (0.00s)
后者其实相对比较好理解,在匿名函数里修改的还是ret的值,相当于在ret=1的操作后面添加了ret=3,所以返回值必然是3.
前者这个匿名函数虽然可以接受ret=1的入餐,但他此时修改的变量r,则是值传递复制过来的一个与ret相等的新变量。因此不影响回传。