Go: You Should Know This Before Using Defer

Defer is one of my favorite things in Go. You can use it to make sure operation is executed before a function returns. It is very useful. I think almost all Go programmer, if not all, has used defer statement in their application. But there is one thing you should know before using defer in Go.

So what is it that you should know before using defer? It is that the deferred function’s parameter is evaluated immediately even though the function is executed before the containing function returns. It is written in the specification and in the Tour of Go, but I think some of the people that use defer didn’t know it at first (including me haha). So what does it mean? Let’s see it in action.

I use this code to try it:

    func myFunc() {
        myStr := "intial"
        defer deferedFunc(myStr)

        myStr = "altered"
        fmt.Println("myFunc's myStr: ", myStr)
    }

    func deferedFunc(str string) {
        fmt.Println("deferedFunc's str: ", str)
    }

There are two functions, myFunc and deferedFunc. The myFunc declare and initiate a string variable, then use defer with the string as a parameter to deferedFunc, then change the value of the string and print the value of the string. The deferedFunc will print the given parameter.
Run the code and I got this in the terminal:

myFunc's myStr:  altered
deferedFunc's str:  intial
Do you see that the fmt.Println in the deferedFunc is executed last, but it has the parameter of the initial string value. The string’s value is changed before the deferred function is called, but the deferred function got the initial value because it is evaluated immediately when defer is used.

So what if we want to pass the latest value to the deferedFunc? To do this, We can defer anonymous function that call the deferedFunc.

    func myFunc() {
        myStr := "intial"
        defer func() {
            deferedFunc(myStr)
        }()

        myStr = "altered"
        fmt.Println("myFunc's myStr: ", myStr)
    }

    func deferedFunc(str string) {
        fmt.Println("deferedFunc's str: ", str)
    }
You will get:
myFunc's myStr:  altered
deferedFunc's str:  altered

I think this is important to know because you can avoid unexpected bugs when using defer. You can also share this information with others and prevent your colleagues to make unexpected bugs.

go 

See also