go语言defer系列(一)

192 阅读2分钟

「这是我参与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相等的新变量。因此不影响回传。