defer和return的执行顺序

  在讨论defer和return的执行顺序前,必须先了解return的机制,在Go语言里,return实际上是分两步执行的。
 
  以匿名返回值为例,当执行到“return 变量名”时,这两步依次是:
 
  1、把返回值保存到临时变量t里(临时变量t对开发者是透明的);
 
  2、把t的值返回给函数调用处。
 
  命名返回值也是类似的,只不过由于返回值已经有变量保存了,所以不需要使用临时变量,也就是返回值变量本身就充当了临时变量的角色。
 
  明白了return的机制就可以说结论了,return分两步执行,而defer是在return的两步之间执行。
 
  正是由于defer夹在return的两步之间执行,如果在defer里对返回值进行修改就有可能影响最终的返回结果,而且会让代码的可读性变差,所以defer应尽量用于释放资源操作(如关闭文件、关闭查询结果集等),而不是在defer里做逻辑运算操作。

  下面是两个使用defer的例子:

package main

import "fmt"

func test(num int) int {
   defer func() {
      num = 2048
   }()

   // 重要说明:执行return时会先把num的值保存一个到临时变量,然后转去执行defer的匿名函数,最后才把临时变量的值返回给函数调用处,
   //           注意是临时变量的值,而不是num的值。
   return num
}

func main() {
   fmt.Printf("num = %+v \n", test(1024)) // num = 1024
}

//========== 总结 ==========//
// 函数使用匿名返回值时,可以无视所有的defer,即return后面的变量值就是最终返回值,在defer里修改该变量值也不会影响返回值。

package main

import "fmt"

func test(num int) (i int) {
   defer func() {
      num = 2048
      i = num
   }()

   // 重要说明:执行到return时,会先转去执行defer的匿名函数,然后才把i的值返回给函数调用处,所以在defer里如果修改了i的值也
   //           会影响最终返回结果。
   return
}

func main() {
   fmt.Printf("num = %+v \n", test(1024)) // num = 2048
}

//========== 说明 ==========//
// 函数使用命名返回值时,在defer里修改返回值变量会影响最终返回结果。

Copyright © 2024 码农人生. All Rights Reserved