Go Defer Simplified with Practical Visuals

Learn about Golang’s defer statement with various usage examples.

Inanc Gumus
Learn Go Programming
5 min readNov 23, 2017

--

What is defer?

It takes a func and executes it just before* the surrounding func returns whether there is a panic or not.

Prints: “first” than “later”

👉 Go doesn’t need destructors because it has no built-in constructors. This is a good balance.

👉 Defer resembles to finally” however the defer belongs to the surrounding “func” while the finally belongs to an exception “block”.

👊 Bonus: See my comment here about the internals of defer if you’re curious about how it works. It’s actually been documented pragmatically as “it runs — after — the surrounding func returns”, however, there are some other inner details.

Releasing acquired resources

Defer funcs are often used to release the acquired resources inside a func.

The func closes the opened file whether there is an error or not on all returns — marked with the star.

Save us from panic

Defer can recover from a panic to prevent the termination of a program if the panic emitted from the same goroutine.

recover() returns the value provided to panic() which lets you decide what you’d do with it. You can also pass an error or other types of values to panic, then you can check whether the panic was caused by the value you’re looking for. More here.

Deferred closure

A deferred func can be of any type of func. So, when used with an anonymous funcobviously — the func becomes aware of its surroundings.

Notice that it sees the latest state of the surrounding values, check out:

Understand how a defer func sees its context

Params evaluation

Go runtime will save any passed params to the deferred func at the time of registering the defer— not when it runs.

Example

Declare a dummy func that registers a deferred closure. It also uses a named result value “n” to increase the passed number for the second time:

func count(i int) (n int) {  defer func(i int) {
n = n + i
}(i)
i = i * 2
n = i
return
}

Let’s try:

count(10)// output: 30

What happened?

Parse the visual following the numbers (on the left): 1, 2, 3.

In some situations defer can help you to change the result value before the return by using the named result values as seen in the example.

Multiple defers

Multiple defers are saved in a stack list. So, the last registered defer will run as the first. Beware: Using multiple defers may hinder the readability.

Example

Output

first
last
To understand how multiple defers work

Watch how it works

The animation loops

Deferred methods

You can also use methods with defer. However, there’s a quirk. Watch.

Without pointers

type Car struct {
model string
}
func (c Car) PrintModel() {
fmt.Println(c.model)
}
func main() {
c := Car{model: "DeLorean DMC-12"}
defer c.PrintModel() c.model = "Chevrolet Impala"
}

Output

DeLorean DMC-12

With Pointers

func (c *Car) PrintModel() {
fmt.Println(c.model)
}

Output

Chevrolet Impala

What’s going on?

Remember that the passed params to a deferred func are saved aside immediately without waiting for the deferred func to be run.

So, when a method with a value-receiver is used with defer, the receiver will be copied (in this case Car) at the time of registering and the changes to it wouldn’t be visible (Car.model). Because the receiver is also an input param and evaluated immediately to “DeLorean DMC-12” when it’s registered with the defer.

On the other hand, when the receiver is a pointer when it’s called with defer, a new pointer is created but the address it points to would be the same with the “c” pointer above. So, any changes to it would be reflected flawlessly.

Alright, that’s all for now. Thank you for reading so far.

Let’s stay in touch:

--

--