SoFunction
Updated on 2025-03-05

Detailed explanation of the usage of Golang keyword defer

1. A brief introduction and usage scenarios of defer

defer is a keyword in Go, used before a method or function, as a delayed call to a method or function. It is mainly used in the followingTwo scenes

  • Elegantly release resources, such as some network connections, database connections, and file resources.
  • Cooperate with recover to handle panic exceptions

Scene 1: Copy the file

func CopyFile(dstFile, srcFile string) (wr int64, err error) { 
    src, err := (srcFile)     
    if err != nil {         
        return     
    }      
    dst, err := (dstFile)     
    if err != nil {         
        return    
    }      
    wr, err = (dst, src)     
    ()     
    ()     
    return 
}

There is a problem with such code. When the execution fails on line 6, the program returns but does not close the src that was successfully opened in the previous section. The resource is not closed correctly. The correct code is as follows:

(If the resource is successfully opened and err is not returned, you can use defer to close the resource elegantly)

func CopyFile(dstFile, srcFile string) (wr int64, err error) {
    src, err := (srcFile)
    if err != nil {
        return
    }
    defer ()
    dst, err := (dstFile)
    if err != nil {
        return
    }
    defer ()
    wr, err = (dst, src)  
    return err
}

Scene 2: Handle exception panic

package main
import "fmt"
func main() {
   defer func() {
      if r := recover(); r != nil {
         (r)
      }
   }()
   a := 1
   b := 0
   ("result:", a/b)   
}

Running results:

runtime error: integer divide by zero

The program does not output result, and will throw panic, because it cannot divide a number with a divisor of 0. We use defer to catch exceptions when panic occurs on the program. In go, panic is used to throw exceptions, recover catches exceptions, and the exceptions will be analyzed in the next go article.

Through these two usage scenarios, we can also see the time when the function followed by defer is called:

  • When the function returns
  • When the current coroutine panic occurs

Speaking of the timing of delayed functions being called, then by the way, let’s talk about the execution order of multiple delayed functions being called. The official explanation of defer writes that every time the defer statement is executed, the function will be "Press the stack",at the same timeFunction parametersWill becopyCome down

These two points are very important:

First, it means that when there are multiple defers in a function, the execution order is LIFO.First in and then out

The second is to explain the parameters of the delay functionWhen the defer statement appearsIt's already confirmed

This code is used to supplement these two points: see if you can understand the execution results, and if you can, just jump to the second part

func deferRun1() {
   var num = 1
   numptr := &num
   defer ("defer run 1: \n", numptr, num, *numptr) // 0xc000022088 1 1
   defer func() {
      ("defer run 2 : \n", numptr, num, *numptr) // 0xc000116028 2 2
   }()
   defer func(num int, numptr *int) {
      ("defer run 3: \n", numptr, num, *numptr) //0xc000116028 1 2
   }(num, numptr)
   num = 2
   (numptr, num, *numptr) // 0xc000116028 2 2
   return
}

For the first defer, the passed num and *numptr are both specific values, so the result will be 1,1 after the program returns

For the second defer, no parameters are passed in, and the result will be the same as the last num and numptr addresses.

For the third defer, the passed parameter num is a fixed value, while numptr is a fixed address. The content corresponding to the subsequent address has been modified, so the result is 1,2

At the same time, the execution order will be the third defer executed first, and then the second...

2. Defer execution time in return

The first section mentioned above is a case where the delay function is executed when returning. To be more specific, what results does the defer operation bring about when returning?

In one sentence, solve this problem: the function return is not an atomic operation, and it requires the following three steps:

  • Set the return value
  • Execute defer
  • Return the result

It is necessary to keep it in mind and strictly follow these three steps when analyzing, otherwise it will be very easy.Fall out of pit

The following list of the situations that need attention:

Situation 1: The delay function parameters have been fixed long ago (an important point mentioned in the first section)

func main() {
   deferRun()
   deferRun2()
}
func deferRun() {
   var num = 1
   defer ("num is %d\n", num)
   num = 2
   return
}
func deferRun2() {
   var arr = [4]int{1, 2, 3, 4}
   defer printArr(&arr)
   arr[0] = 100
   return
}
func printArr(arr *[4]int) {
   for i := range arr {
      (arr[i])
   }
}

Running results are num is 1 and 100,2,3,4

The reason is very simple:The parameters of the delay function are fixed when defer appears, For deferRun, the passed parameter is the value of num, while for deferRun, the passed parameter is the address of arr,The address remains unchanged, but the content corresponding to the address is modified, so the output is changed.

Situation 2:Strictly grasp the three steps and bypass"anonymous"Small pit, analyze and read the comments

func main() {
   res1 := deferRun()
   (res1)
   res2 := deferRun2()
   (res2)
   res3 := deferRun3()
   (res3)
   res4 := deferRun4()
   (res4)
}
// return 2 
func deferRun() (res int) {
   num := 1
   defer func() {
      res++
   }()
   return num   //1. The return value parameter res is assigned to num, that is, 1 //2. Execute the defer statement, res++ //3. The result returns 2}
// return 1
func deferRun2() int {   // Note that the return value here is anonymous. We can name the anonymous return value in our hearts as res   var num int
   defer func() {
      num++
   }()
   return 1  //1. The anonymous return value parameter (res) is assigned to 1 //2. Execute the defer statement, num++, add a loneliness //3. The result returns 1}
//return 1
func deferRun3() int {
   num := 1
   defer func() {
      num++
   }()
   return num  //1. Assign anonymous (res) to num that is 1 //2. Execute the defer statement, num++, add a loneliness, num is 2, res is still 1              //3. The result returns 1}
//return 2
func deferRun4() (res int) {
   num := 1
   defer func() {
      res++
   }()
   return num //1. The return value parameter res is assigned to 1 //2. Execute the defer statement, res++ //3. The result returns 2}

The execution result is 2 1 1 2

3. Summary

Defer is elegant, can release resources in a simple way, and can also cooperate with recover to handle panic exceptions

The parameters of the delay function defined by defer are determined when the defer statement appears. Note that sometimes the address is determined.

Return is not an atomic operation, it includes three important steps, the order is: (note the anonymous return value parameter)

Set return value parameters ->Execute defer statement ->Return result

This is the end of this article about the detailed explanation of the usage of Golang keyword defer. For more relevant Golang keyword defer content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!