Go中理解defer和return顺序
我们通过示例来理解defer
和 return
之前的到底是怎么运行的.
package main
import "fmt"
func WithoutNameReturnValue() int {
var i int
defer func() {
i++
fmt.Println("defer2 in WithoutNameReturnValue",i)
}()
defer func() {
fmt.Println("defer1 in WithoutNameReturnValue",i)
}()
return i
}
func WithNameReturnValue() (j int) {
defer func() {
j++
fmt.Println("defer2 in WithNameReturnValue", j)
}()
defer func() {
j++
fmt.Println("defer1 in WithNameReturnValue", j)
}()
return j
}
func main() {
fmt.Println(WithoutNameReturnValue())
fmt.Println(WithNameReturnValue())
}
执行结果:
defer1 in WithoutNameReturnValue 0
defer2 in WithoutNameReturnValue 1
0
defer1 in WithNameReturnValue 1
defer2 in WithNameReturnValue 2
2
我们发现defer
在匿名返回值和命名返回值函数中的不同表现, 在命名返回值的函数的返回值被defer
修改了.
在 Go Tour中, 官方在介绍defer
时是这么说的:
A defer statement defers the execution of a function until the surrounding function returns.
经查阅官方资料blog.golang.org,发现defer
的执行有以下三个规则:
1: A deferred function’s arguments are evaluated when the defer statement is evaluated. (示例如下:)
// print 0
func a() {
i := 0
defer fmt.Println(i)
i++
return
}
解析defer
执行的函数如果有传参,defer
里面为i
的副本.所以print 0, 如果是传递指针会则会改变.
2: Deferred function calls are executed in Last In First Out order after the surrounding function returns.(示例如下)
// This function prints "3210"
func b() {
for i := 0; i < 4; i++ {
defer fmt.Print(i)
}
}
3: Deferred functions may read and assign to the returning function’s named return values.(示例如下)
// This function returns 2
func c() (i int) {
defer func() { i++ }()
return 1
}
其中第三点就是文中代码结果为2
的原因. 那么为什么结果1
中defer
没有修改返回值呢?我们可以推测,其实在函数最终返回前,defer
函数已经执行了,在结果1
中,由于defer
函数是对函数内部变量i
进行操作,所有没有影响.在结果2
中,由于返回值被提前声明,所以defer
函数能够在return
语句对返回值赋值之后,继续对返回值操作.
函数的整个返回过程是:
return
对返回变量赋值, 如果是匿名返回值就先声明在赋值.- 执行
defer
函数return
携带返回值返回.
换种方式解释下, return
返回值的运行机制:
return
并非原子操作,分为赋值和返回两步操作
匿名返回值实际上return
了一个返回值(假设为s
),先将i 赋值给s
,后续的操作因为都是针对i
进行的,所以不影响s
,此后因为s
不更新, 所以return s
不变。
相当于:
var i int
s := i
return s
命名返回值同上
s
相当于命名的变量i
, 因为所有的操作都是基于命名变量i(s)
,返回也是i
,所以每一次defer
操作,都会更新返回值i
.
「真诚赞赏,手留余香」
真诚赞赏,手留余香
使用微信扫描二维码完成支付