Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When defer func evaluates its parameters

I am learning how defer behaves in golang, and want to use it to handle error when the function returns.

Code is as below:

package main

import "fmt"
import "errors"

func main() {
    a()
}

func a() {
    var err error   
    defer func(){
        if err != nil {
            fmt.Printf("1st defer: %s\n", err)
        } else {
            fmt.Println("1st defer: defer not error")
        }
    }()
    defer func(err error){
        if err != nil {
            fmt.Printf("2nd defer: %s\n", err)
        } else {
            fmt.Println("2nd defer: defer not error")
        }
    }(err)

    err = errors.New("new error")
    if err != nil {
        return
    }
}

The output:

2nd defer: defer not error
1st defer: new error

Doc says parameters are evaluated when the defer call is evaluated, which seems it should be consistent. Why 2 defer has different value for variable err and thusly different output? I know it is related to 2nd function has err as input parameter, but don't know why.

like image 736
Yulong Avatar asked Mar 09 '17 19:03

Yulong


People also ask

What is a defer func?

In Go language, defer statements delay the execution of the function or method or an anonymous method until the nearby functions returns. In other words, defer function or method call arguments evaluate instantly, but they don't execute until the nearby functions returns.

What is the point of Defer in Golang?

In Golang, the defer keyword is used to delay the execution of a function or a statement until the nearby function returns. In simple words, defer will move the execution of the statement to the very end inside a function.

Is defer called before return?

A defer statement defers the execution of a function until the surrounding function returns. The deferred call's arguments are evaluated immediately, but the function call is not executed until the surrounding function returns.

Does defer run in panic?

Panics happen after a deferred statement is executed. the program ends after the panic statement is run.


2 Answers

There is another similar situation there is in case of Defer Statement and Defer Function. Please have look at the example below

package main

import (
    "fmt"
    "time"
)

func main() {

    start := time.Now()
    time.Sleep(3*time.Second)
    defer func() { fmt.Println("Defer Function Elapsed Time: ", time.Since(start)) }() //Defer Function
    defer fmt.Println("Defer Statement Elapsed Time: ", time.Since(start)) //Defer Statement
    time.Sleep(3*time.Second)
}

Output:

Defer Statement Elapsed Time: 3s

Defer Function Elapsed Time: 6s

Try above in go play

This is because of in the Deferred Statement case the deferred call's arguments are evaluated immediately refer doc

like image 152
arunjos007 Avatar answered Dec 08 '22 17:12

arunjos007


Another way is by using reference to original err variable

package main

import (
    "errors"
    "fmt"
)

func main() {
    a()
}

func a() {
    var err error
    defer func() {
        if err != nil {
            fmt.Printf("1st defer: %s\n", err)
        } else {
            fmt.Println("1st defer: defer not error")
        }
    }()
    defer func(err *error) {
        if *err != nil {
            fmt.Printf("2nd defer: %s\n", *err)
        } else {
            fmt.Println("2nd defer: defer not error")
        }
    }(&err)

    err = errors.New("new error")
    if err != nil {
        return
    }
}

And output is:

2nd defer: new error
1st defer: new error
like image 31
HubertS Avatar answered Dec 08 '22 19:12

HubertS