Kyle Banks

When to Use Defer in Go

Written by @kylewbanks on Aug 27, 2016.

Defer is a powerful control flow mechanism that is unique to Go (at least until Swift 2.0 added it). It allows you to defer a function call to the end of the currently executing function no matter how or where it returns. This is useful for many reasons, the most common of which are to close an open connection or unlock a Mutex immediately before the function ends.

For example, you’ll commonly see code like the following:

func BillCustomer(c *Customer) error {
    c.mutex.Lock()
    defer c.mutex.Unlock()
    
    if err := c.Bill(); err != nil {
        return err
    }
    
    if err := c.Notify(); err != nil {
        return err
    }
    
    // ... do more stuff ...
    
    return nil
}

The defer in this example ensures that no matter how BillCustomer returns, the mutex will be unlocked immediately prior to BillCustomer returning. This is extremely useful because without defer you would have to remember to unlock the mutex in every place that the function could possibly return.

The closest construct to defer in any other programming languages that I’m aware of would be the finally block of a try/catch in languages like Java, however the distance of the finally block from the logic that it actually relates to makes it more complicated to read in my opinion. With defer, you can declare the defer immediately beneath the resource that it pertains to, or the logic that it depends on, for example.

Multiple Defers

defer can also be used more than once in a single function. When you call defer, the function call being deferred is added to a list and all function calls in the list will be executed prior to the function returning. This isn’t necessarily, in my opinion, a good practice because it isn’t immediately obvious what order the deferred functions will be executed in.

For instance, can you guess the output from the following program:

package main

import (
	"fmt"
)

func main() {
	defer func() {
		fmt.Println("Defer 1")
	}()
	defer func() {
		fmt.Println("Defer 2")
	}()
	
	fmt.Println("I'm not deferred!")
}

You may expect something along the lines of:

$ go run defers.go
I'm not deferred!
Defer 1
Defer 2

If this is what you’re expecting, you’d be incorrect. The reason is that the deferred functions are executed in the reverse order that they are deferred! This goes against what we are typically accustomed to as developers, where your code is executed more-or-less in the same sequence it is written (aside, perhaps, from the callback hell of JavaScript).

The actual output of the program above is:

$ go run defers.go
I'm not deferred!
Defer 2
Defer 1

Because of this, my personal opinion is not to use multiple defer statements in a single function. It becomes too difficult to pinpoint exactly what is going to happen when a function executes at first glance. A single defer is easy enough to understand, especially in the common use-cases of releasing a resource, but multiple deferred functions can quickly become a chore to understand.

When to Defer

Like I said before, defer is especially useful for releasing resources that must be released under any conditions.

I recently posted a thread on Reddit regarding my post on mutexes in Go, and a discussion broke out regarding the use of defer. The argument against it is that it’s an expensive operation and reduces performance of your applications.

However, this performance hit is measured in nanoseconds! I strongly feel that it’s important to never prematurely optimize your applications, especially when it reduces readability and maintainability of your code. To me a performance hit measured in nanoseconds is absolutely worth the improved reliability of a function in almost all situations. Sure if you’re writing software where every single nanosecond lost is a deal-breaker, go ahead and avoid defer - there is a measurable impact to using it. However, in almost all applications, there are many more beneficial performance improvements you can make prior to removing defer from your codebase.

In my opinion, defer is an extremely powerful feature of the Go language, and should be used whenever it’s appropriate. For instance, in the following example, you likely don’t need to bother with a defer:

func Add(i int) {
    mu.Lock()
    defer mu.Unlock()
    val += i
}

There’s just no need to use it for such a trivial function, and you’re incurring that (minuscule) performance hit for absolutely zero benefit. However, if the function is even slightly more complex, I do feel that defer is worth using:

func Divide(i int) error {
    mu.Lock()
    defer mu.Unlock()
    
    if i == 0 {
        return errors.New("Can't divide by zero!")
    }
    
    val /= i
    return nil
}

Another instance where I feel it’s worth using defer is when a simple function has a return. For instance, if the Add function above returned the result, I would personally use a defer:

func Add(i int) int {
    mu.Lock()
    defer mu.Unlock()
    
    return sum + i
}

To me this is just a cleaner way to write the function than:

func Add(i int) int {
    mu.Lock()
    res := sum + i
    mu.Unlock()
    return res
}

But again, this is all personal preference. You should ensure that no matter when and where you choose to use defer, you remain consistent with it. The more places you flip-flop on using defer and not using it, the more care and attention will be required by other developers who need to modify your code. For example if you usually use defer to release connections, but in one function you don’t and a new return case needs to be added, the developer adding the return may assume the connection release is deferred like in the rest of the application, and return without releasing it. This is a very easy and dangerous way to add complexity to your codebase, so take care to ensure you and your team are consistent with your defers.

Let me know if this post was helpful on Twitter @kylewbanks or down below, and follow me to keep up with future posts!