Essential Go Defer  Edit on GitHub      File Issue

Defer gotchas

When using defer keep the following in mind.

Deferred functions are called at function end

Deferred statements have a function scope, not a block scope.

In other words: deferred calls are executed when exiting a function not when executing block created with if or for statements.

func main() {
	fmt.Print("Before if\n")
	if true {
		defer fmt.Print("inside if\n")
	}

	fmt.Print("Ater if\n")
}
Before if
Ater if
inside if

You might expect that deferred statement to be executed when we exit if branch but it’s executed as the last thing in a function.

Deferred function arguments

func main() {
	for i := 0; i < 2; i++ {
		defer func() {
			fmt.Printf("%d\n", i)
		}()
	}
}
2
2

You might have expected that this code will print 0 and 1, because those are the values of i when we evaluate defer.

This is how the code looks like after compiler rewrites the loop to implement defer:

var i int
for i = 0; i < 2; i++ {
}

fmt.Printf("%d\n", i)
fmt.Printf("%d\n", i)

Now it’s clear that when we call deferred fmt.Printf, i is 2.

We can fix this by using a closure to capture the variable:

func main() {
	for i := 0; i < 2; i++ {
		defer func(i2 int) {
			fmt.Printf("%d\n", i2)
		}(i)
	}
}
1
0

A closure is more expensive as it requires allocating an object to collect all the variables captured by the closure. In this case that’s the price of correctness.

Defer/
Defer basics
Defer gotchas
  ↑ ↓ to navigate     ↵ to select     Esc to close