1. 基础特性
1.1 先进后出
对于多个defer语句,类似栈,按先进后出的顺序执行。
1func main() {
2 var whatever [5]struct{}
3 for i := range whatever {
4 defer fmt.Print(i)
5 }
6}
7
8// 43210
1.2 实时解析函数参数
1package main
2
3import "fmt"
4
5func test(a int) {//无返回值函数
6 defer fmt.Println("1、a =", a) //方法
7 defer func(v int) { fmt.Println("2、a =", v)} (a) //有参函数
8 defer func() { fmt.Println("3、a =", a)} () //无参函数
9 a++
10}
11func main() {
12 test(1)
13}
14
15// 3、a = 2
16// 2、a = 1
17// 1、a = 1
1.3 return 返回机制
defer、return、返回值三者的执行逻辑应该是:
- 首先,return 最先执行,return将结果写入返回值中;
- 接着,defer 执行收尾工作;
- 最后,函数携带当前返回值(可能和最初的返回值不相同)退出。
defer 在 return 后
defer放在return后面时,不会被执行。
1func f(i int) int{
2 return i
3 defer fmt.Print("i =", i)
4 return i+1
5}
6
7func main() {
8 f(1)
9}
没有输出,因为return i之后函数就已经结束了,不会执行defer。
无名返回值 🎓
1func a() int {
2 var i int
3 defer func() {
4 i++
5 fmt.Println("defer2:", i)
6 }()
7 defer func() {
8 i++
9 fmt.Println("defer1:", i)
10 }()
11 return i
12}
13
14func main() {
15 fmt.Println("return:", a())
16}
1defer1: 1
2defer2: 2
3return: 0
返回值由变量 i 赋值,相当于返回值=i=0。
第二个defer中i++ = 1, 第一个defer中i++ = 2,所以最终i的值是2。
但返回值已经被赋值了,即使后续修改i也不会影响返回值。最终返回值返回,所以main中打印0。
有名返回值 🎓
1func b() (i int) {
2 defer func() {
3 i++
4 fmt.Println("defer2:", i)
5 }()
6 defer func() {
7 i++
8 fmt.Println("defer1:", i)
9 }()
10 return i //或者直接写成return
11}
12
13func main() {
14 fmt.Println("return:", b())
15}
1defer1: 1
2defer2: 2
3return: 2
此处指明了返回值就是i,所以后续对 i 进行修改都相当于在修改返回值,所以最终函数的返回值是2。
特殊情况
注意下列代码,返回值 r 作为参数传入 defer,虽然有 r = r + 5
,但这是局部变量,与 f()
中的返回值 r 无关,因此该函数仍然返回 1
1func f() (r int) {
2 defer func(r int) {
3 r = r + 5
4 }(r)
5 return 1
6}
函数返回值为地址
本质上和有名返回值一样。
1func c() *int {
2 var i int
3 defer func() {
4 i++
5 fmt.Println("defer2:", i)
6 }()
7 defer func() {
8 i++
9 fmt.Println("defer1:", i)
10 }()
11 return &i
12}
13
14func main() {
15 fmt.Println("return:", *(c()))
16}
1defer1: 1
2defer2: 2
3return: 2
2. Defer 与闭包
for 结束时 t.name="c"
,接下来执行的那些defer语句中用到的t.name
的值均为"c"
。
1type Test struct {
2 name string
3}
4func (t *Test) pp() {
5 fmt.Println(t.name)
6}
7func main() {
8 ts := []Test{{"a"}, {"b"}, {"c"}}
9 for _, t := range ts {
10 defer t.pp()
11 }
12}
13// c
14// c
15// c
修改代码,使用有参参数
defer语句中的参数会实时解析,所以在碰到defer语句的时候就把该时的t代入了。
1type Test struct {
2 name string
3}
4func pp(t Test) {
5 fmt.Println(t.name)
6}
7func main() {
8 ts := []Test{{"a"}, {"b"}, {"c"}}
9 for _, t := range ts {
10 defer pp(t)
11 }
12}
13// c
14// b
15// a
修改代码,在循环中声明新变量
每次都有一个新的变量tt:=t
,所以每次在执行defer语句时,对应的 tt
不是同一个,所以输出的结果也不相同。
1type Test struct {
2 name string
3}
4func (t *Test) pp() {
5 fmt.Println(t.name)
6}
7
8func main() {
9 ts := []Test{{"a"}, {"b"}, {"c"}}
10 for _, t := range ts {
11 tt := t
12 println(&tt)
13 defer tt.pp()
14 }
15}
10xc000010200
20xc000010210
30xc000010220
4c
5b
6a
3. Defer 常见使用场景
3.1 关闭文件
开启文件后,使用 defer 关闭文件。
1func ReadFile(filename string) ([]byte, error) {
2 f, err := os.Open(filename)
3 if err != nil {
4 return nil, err
5 }
6 defer f.close()
7 return ReadAll()
8}
3.2 互斥锁
加锁后,使用 defer 解锁。
1var mu sync.Mutex
2var m = make(map[string]int)
3
4func lookup(key string) int {
5 mu.Lock()
6 defer mu.Unlock()
7 return m[key]
8}
调用os.Exit时defer不会被执行
1func deferExit() {
2 defer func() {
3 fmt.Println("defer")
4 }()
5 os.Exit(0)
6}