Go中defer与return之间的迷之执行顺序

偶然间发现了一个有意思的地方:在使用defer时,匿名返回值的函数和命名返回值的函数的返回结果是不一样的。具体见如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//
// defer_test1.go
// Copyright (C) 2019 bryce <pyplgo@gmail.com>
//
// Distributed under terms of the MIT license.
//

package main

import "fmt"

func WithoutNamedReturnValue() int {
var i int
defer func() {
i++
fmt.Println("defer2 in WithoutNamedReturnValu", i)
}()

defer func() {
i++
fmt.Println("defer1 in WithoutNamedReturnValu", i)
}()

return i
}

func WithNamedReturnValue() (j int) {
defer func() {
j++
fmt.Println("defer2 in WithNamedReturnValu", j)
}()

defer func() {
j++
fmt.Println("defer1 in WithNamedReturnValu", j)
}()

return j
}

func main() {
fmt.Println(WithoutNamedReturnValue())
fmt.Println(WithNamedReturnValue())
}
1
2
3
4
5
6
7
8
9
10
//output
// 结果1
defer1 in WithoutNamedReturnValu 1
defer2 in WithoutNamedReturnValu 2
0

//结果2
defer1 in WithNamedReturnValu 1
defer2 in WithNamedReturnValu 2
2

可以看出,命名返回值的函数的返回值被defer修改了。在Go Tour中,官方在介绍defer时是这么说的:

A defer statement defers the execution of a function until the surrounding function returns.

如果按照这个理解,defer应该没有修改函数返回值的可能,因为defer是until the surrounding function returns才执行的。然后查阅官方给的补充材料,然后发现defer的执行有以下三个规则:

  1. A deferred function’s arguments are evaluated when the defer statement is evaluated.

  2. Deferred function calls are executed in Last In First Out order after the surrounding function returns.

  3. Deferred functions may read and assign to the returning function’s named return values.

这个第三点就是出现文中开头代码结果2的原因。那么为什么结果1中defer没有修改返回值呢?我们可以推测,其实在函数最终返回前,defer函数就已经执行了,在结果1中,由于defer函数是对函数内部变量i进行操作,所以没有影响到返回值。在结果2中,由于返回值已经被提前声明,所以defer函数能够在return语句对返回值赋值之后,继续对返回值进行操作。

总结一下就是,函数的整个返回过程应该是:

  1. return对返回变量赋值,如果是匿名返回值就先声明再赋值;
  2. 执行defer函数;
  3. return携带返回值返回。
  • 本文作者: 大护法
  • 本文链接: https://todebug.com/defer-in-go/
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 4.0 许可协议。转载请注明出处!
0%