Cryptolosophy     About     Articles     Papers     Subscribe

Harnessing LIFO Defer Stacking in Go

[Email] [Tweet] [Subscribe]

One of Go’s many useful features is the defer statement. It allows you to say, “I don’t want to do this right now, but please do it when this function returns”.

Consider the following code:

package main

import "fmt"

func main() {
	fmt.Println("start counting")

	for i := 0; i < 3; i++ {
		defer fmt.Println(i)

	fmt.Println("finished counting")

Deferred statements are executed in last-in-first-out order, so this will output:

start counting
finished counting

You can test it out for yourself on the playground.

But why is this property useful, and how can we use it to our advantage? Well, consider a slightly more complicated example:

// EqualBytes compares the contents of a container with a given slice.
func (b *container) EqualBytes(buf []byte) bool {
    // Attain the mutex.
    defer b.Unlock()

    // If sealed, first unseal.
    if b.sealed {
        defer b.reseal()

    // Compare and return the result.
    return bytes.Equal(b.buffer, buf)

Here we see a comparison method for some object, a *container. This object exposes a mutex and also has multiple states: sealed and unsealed. There’s two defer statements; the first releases the mutex and the second reseals the container.

The obvious problem (?) is that after the function returns, there may be a delay before the container is resealed and so another thread could mistakenly access or modify the unsealed container before we have a chance to reseal it.

Or could it? Look again. With LIFO, the order of execution will be:

// -> function returns


The way defer is designed made it so that we still own the mutex when we unseal, and so as long as we designed the rest of our API correctly, there should be no accidental access issues in multi-threaded applications.

It’s worth thinking about this useful property the next time you use defer in your code, and really taking advantage of it.