The Go language’s delay function defer acts as a try… However, in practice, a lot of Gopher didn’t really understand the sequence between defer, return, return value, and Panic, so they fell into the trap. Today, we are going to uncover the mystery!

To start, run the following two pieces of code:

A. The case of anonymous return values

package mainimport (
    "fmt")func main(a) {
    fmt.Println("a return:". a()) // Prints a return: 0}func a(a) int {
    var i int
    defer func(a) {
        i++
        fmt.Println("a defer2:". i) // 打印结果为 a defer2: 2
    } ()
    defer func(a) {
        i++
        fmt.Println("a defer1:". i) // 打印结果为 a defer1: 1
    } ()
    return i}Copy the code

B. The case where a name returns a value

package mainimport (
    "fmt")func main(a) {
    fmt.Println("b return:". b()) // Prints b return: 2}func b(a) (i int) {
    defer func(a) {
        i++
        fmt.Println("b defer2:". i) // 打印结果为 b defer2: 2
    } ()
    defer func(a) {
        i++
        fmt.Println("b defer1:". i) // 打印结果为 b defer1: 1
    } ()
    return i // Or return directly has the same effect}Copy the code

Let’s start with the hypothesis (which is the correct conclusion) to help you understand why:

  1. Multiple deferred defer were executed in “last in, first out/First in, last out” order;

  2. All functions check for the existence of the DEFER statement before executing the RET return instruction, and if so, call the defer statement in reverse order to wrap things up before exiting and returning.

  3. Anonymous return values are declared at return execution, and named return values are declared at the same time as the function declaration. Therefore, only named return values can be accessed in the defer statement, not the anonymous return values directly.

  4. The first step is to assign the value to a named value, and the second step is to declare and then assign the value to an anonymous value. The second step is to call the RET return directive and pass in the return value. RET checks to see if defer exists. If so, it interrupts the defer statement in reverse order, and RET exits the function with the return value.

Therefore, the order of defer, return, and return value should be: Return assigns the return value first; Then defer started to do some finishing work; Finally, the RET directive exits the function with the return value.

How to explain the difference between the two results:

The difference between the two pieces of code can be easily understood from the conclusion above.

  • The return value of the a()int function has not been named in advance. It was assigned to another variable, and what deferred modified was another variable (in fact, it did not have direct access to the return value), so the return value was not modified when the function exited.

  • The return value of the b()(I int) function was named ahead of time, which allowed defer to access it, so after the return assignment returned value I, defer called the return value I and modified it, Finally, it causes the return to call RET to exit the function and return the value that defer modified.

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

package mainimport (
    "fmt")func main(a) {
    c: =c(a)
    fmt.Println("c return:". *c. c) // C return: 2 0xC082008340 is printed}func c(a) *int {
    var i int
    defer func(a) {
        i++
        fmt.Println("c defer2:". i. &i) // The output is C DEFER2:2 0xC082008340
    } ()
    defer func(a) {
        i++
        fmt.Println("c defer1:". i. &i) // The output is C DEFER1:1 0xC082008340
    } ()
    return &i}Copy the code

Although the return value of c()int was not declared in advance, since the return value of C ()int is a pointer variable, after the return assigned the address of variable I to the return value, defer modified the actual value of I in memory again. Therefore, when return calls RET to exit the function, the return value is still the original pointer address, but the actual memory value it points to has been successfully modified.

That is, we assume that the conclusion is correct!

[D].While the values of the parameters are evaluated when defer declares, it is only the body of its function that defer execution.

package mainimport (
    "fmt"
    "time")func main(a) {
    defer P(time.Now())
    time.Sleep(5e9)
    fmt.Println("main ". time.Now())}func P(t time.Time) {
    fmt.Println("defer". t)
    fmt.Println("P ". time.Now())}// 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 CSTCopy the code

E. The scope of defer

  1. Defer is only valid for the current coroutine (main can be thought of as the main coroutine);

  2. When panic occurs on any of the (main) coroutines, defer, which was declared before panic in the current coroutine, is performed.

  3. In the (main) coroutine where panic occurred, if none of the defer calls recover() to recover, the entire process would crash after the last declared defer was executed;

  4. When os.exit (int) is actively called to Exit the process, defer will no longer be executed.

package mainimport (
    "errors"
    "fmt"
    "time"
    // "os")func main(a) {
    e : = errors.New("error")
    fmt.Println(e)
    // (3) Panic (e) // defer will not execute
    // (4) os.exit (1) // defer will not execute
    defer fmt.Println("defer")
    // (1) go func() {panic(e)}() // will cause defer not to execute
    // (2) Panic (e) // defer will execute
    time.Sleep(1e9)
    fmt.Println("over.")
    // (5) os.exit (1) // defer will not execute}Copy the code

F. defer expressions are called in a first-in, last-out fashion

The defer expression is placed in a stack-like structure, so the order of calls is fifO/LIFO.

The following code outputs 4321 instead of 1234.

package mainimport (
    "fmt")func main(a) {
    defer fmt.Print(1)
    defer fmt.Print(2)
    defer fmt.Print(3)
    defer fmt.Print(4)}Copy the code

Tea station

A place where you can stop and take a look and help you out between tea breaks.

This is about back-end technology, personal management, team management, and other personal considerations.