Golang defer案例

Golang defer案例讲解

Go 语言 中的 defer 语句用于延迟 函数 的调用,每次 defer 都会把一个函数压入栈中,函数返回前再把延迟的函数取出并执行。Golang 中的 defer 可以帮助我们处理容易忽略的问题,如资源释放、连接关闭等。

golang 中 defer 规则可以总结为:延迟函数的参数在 defer 语句出现时就已经确定下来了、延迟函数执行按后进先出顺序执行,即先出现的 defer 最后执行、延迟函数可能操作主函数的具名返回值。

Golang defer规则说明

defer 是在 return 之前执行的。这个在官方文档中是明确说明了的。要使用 defer 时不踩坑,最重要的一点就是要明白,return xxx 这一条语句并不是一条原子指令!

函数返回的过程是这样的:先给返回值赋值,然后调用 defer 表达式,最后才是返回到调用函数中。defer 表达式可能会在设置函数返回值之后,在返回到调用函数之前,修改返回值,使最终的函数返回值与你想象的不一致。

其实使用 defer 时,用一个简单的转换规则改写一下,就不会迷糊了。改写规则是将 return 语句拆成两句写,return xxx 会被改写成:

返回值 = xxx 调用defer函数 空的return

案例一

package main import ( "fmt" ) func func1() (result int) { defer func() { result++ }() return 0 } func main() { fmt.Println("嗨客网(www.haicoder.net)") res := func1() fmt.Println("Res =", res) }

此时,我们运行程序,控制台输出如下:

14_Go语言defer案例.png

上述代码,我们可以修改为如下:

func func1() (result int) { result = 0 //return语句不是一条原子调用,return xxx其实是赋值+ret指令 func() { //defer被插入到return之前执行,也就是赋返回值和ret指令之间 result++ }() return }

因此,其返回值为 1。

案例二

package main import ( "fmt" ) func func2() (r int) { t := 5 defer func() { t = t + 5 }() return t } func main() { fmt.Println("嗨客网(www.haicoder.net)") res := func2() fmt.Println("Res =", res) }

此时,我们运行程序,控制台输出如下:

15_Go语言defer案例.png

上述代码,我们可以修改为如下:

func func2() (r int) { t := 5 r = t //赋值指令 func() { //defer被插入到赋值与返回之间执行,这个例子中返回值r没被修改过 t = t + 5 } return //空的return指令 }

因此,其返回值为 5。

案例三

package main import ( "fmt" ) func func3() (r int) { defer func(r int) { r = r + 5 }(r) return 1 } func main() { fmt.Println("嗨客网(www.haicoder.net)") res := func3() fmt.Println("Res =", res) }

此时,我们运行程序,控制台输出如下:

16_Go语言defer案例.png

上述代码,我们可以修改为如下:

func func3() (r int) { r = 1 //给返回值赋值 func(r int) { //这里改的r是传值传进去的r,不会改变要返回的那个r值 r = r + 5 }(r) return //空的return }

因此,其返回值为 1。

Golang defer案例总结

defer 是在 return 之前执行的。这个在官方文档中是明确说明了的。要使用 defer 时不踩坑,最重要的一点就是要明白,return xxx 这一条语句并不是一条原子指令。

函数返回的过程是这样的:先给返回值赋值,然后调用 defer 表达式,最后才是返回到调用函数中。defer 表达式可能会在设置函数返回值之后,在返回到调用函数之前,修改返回值。