SoFunction
Updated on 2025-03-05

Defer example in Go language

Preface

Everyone knows that the go language defer function is very powerful and is very convenient for resource management, but if it is not used well, there will be traps. In the Go language, the delay function defer serves as the important task of try...catch and is very easy to use. However, in actual applications, many gophers do not really understand the execution order between defer, return, return value, and panic, and thus fall into the pit. Today we will unveil its mystery! Without further ado, let’s take a look at the detailed introduction.

Let's run the following two pieces of code:

A. Anonymous return value situation

package main

import (
 "fmt"
)

func main() {
 ("a return:", a()) // Print result is a return: 0}

func a() int {
 var i int
 defer func() {
  i++
  ("a defer2:", i) // Print result is a defer2: 2 }()
 defer func() {
  i++
  ("a defer1:", i) // Print result is a defer1: 1 }()
 return i
}

B. Case of famous return value

package main

import (
 "fmt"
)

func main() {
 ("b return:", b()) // Print result is b return: 2}

func b() (i int) {
 defer func() {
  i++
  ("b defer2:", i) // Print result is b defer2: 2 }()
 defer func() {
  i++
  ("b defer1:", i) // Print result is b defer1: 1 }()
 return i // Or directly return the same effect}

Let’s first assume the conclusion (this is the correct conclusion) to help everyone understand the reasons:

  • The execution order of multiple defers is "Last in First out/First in Later out";
  • Before executing the RET return instruction, all functions will first check whether the defer statement exists. If it exists, the defer statement will be called in reverse order for the final work and then exit and return;
  • The anonymous return value is declared when the return is executed, and the name return value is declared at the same time as the function is declared. Therefore, in the defer statement, only the name return value can be accessed, but the anonymous return value cannot be accessed directly;
  • return actually should include two steps: the first step is to assign a value to the return value (if it is a name return value, it is directly assigned; if it is an anonymous return value, it is declared first and then assigned); the second step is to call the RET return instruction and pass in the return value, and RET will check whether defer exists. If it exists, insert the defer statement in reverse order first, and finally RET will exit the function with the return value;

Therefore, the execution order of defer, return and return value should be:return first assigns a value to the return value; then defer starts to perform some final work; finally the RET instruction takes the return value and exits the function.

How to explain the difference between the two results:

The reason why the return results of the above two codes are different is actually easy to understand from the above conclusions.

  • a()intThe return value of the function is not known in advance, and its value comes from the assignment of other variables, and the modified variables in defer are also other variables (in fact, the defer cannot directly access the return value), so the return value when the function exits is not modified.
  • b()(i int) The return value of the function is named in advance, which allows defer to access the return value. Therefore, after the return value is assigned to the return value i, defer calls the return value i and makes modifications, and finally causes the return value after the return call RET exits the function to be the value modified by defer.

C. Let’s look at the third example below to verify the above conclusion:

package main

import (
 "fmt"
)

func main() {
 c:=c()
 ("c return:", *c, c) // The print result is c return: 2 0xc082008340}

func c() *int {
 var i int
 defer func() {
  i++
  ("c defer2:", i, &i) // The print result is c defer2: 2 0xc082008340 }()
 defer func() {
  i++
  ("c defer1:", i, &i) // The print result is c defer1: 1 0xc082008340 }()
 return &i
}

Althoughc()intThe return value ofc()int The return value of 'is a pointer variable. After returning assigns the address of variable i to the return value, defer modifies the actual value of i in memory again. Therefore, although the return value when returning calls RET to exit the function is still the original pointer address, the actual value it points to has been successfully modified.

That is, the conclusion we assume is correct!

D. Add one thing: When defer is declared, the value of the defer parameter will be calculated first, and defer will only postpone its function body.

package main

import (
 "fmt"
 "time"
)

func main() {
 defer P(())
 (5e9)
 ("main ", ())
}

func P(t ) {
 ("defer", t)
 ("P ", ())
}

// Output result:// main 2017-08-01 14:59:47.547597041 +0800 CST
// defer 2017-08-01 14:59:42.545136374 +0800 CST
// P  2017-08-01 14:59:47.548833586 +0800 CST

The scope of E. defer

  1. defer is only valid for the current coroutine (main can be regarded as the main coroutine);
  2. When panic occurs in any (main) coroutine, the defer declared previously in panic in the current coroutine will be executed;
  3. In the (main) coroutine where panic occurs, if there is no defer callrecover()Recovery will cause the entire process to crash after executing the last declared defer;
  4. Actively call(int) When exiting the process, defer will no longer be executed.
package main

import (
 "errors"
 "fmt"
 "time"
 // "os"
)

func main() {
 e := ("error")
 (e)
 // (3) panic(e) // defer will not be executed // (4)(1) // defer will not be executed defer ("defer")
 // (1) go func() { panic(e) }() // will cause defer to not be executed // (2) panic(e) // defer will execute (1e9)
 ("over.")
 // (5)(1) // defer will not be executed}

The order of call of the F. defer expression is executed in the first-in and then out mode.

The defer expression will be placed in a structure similar to a stack, so the order of calls is first-in, later-out/last-in, first-out.

The result output of the following code is 4321 instead of 1234.

package main

import (
 "fmt"
)

func main() {
 defer (1)
 defer (2)
 defer (3)
 defer (4)
}

Summarize

The above is the entire content of this article. I hope that the content of this article has certain reference value for everyone's study or work. If you have any questions, you can leave a message to communicate. Thank you for your support.