Understanding defer in Go: Best Practices, Common Pitfalls, and Why It Matters



This content originally appeared on DEV Community and was authored by Abhishek Kumar

Understanding Go’s defer Statement: A Complete Guide

Have you ever written a Go code? Of course you have – that’s why this post is recommended to you! And if your answer is yes, then you might have come across the keyword defer. So let’s understand this in detail so that we can write better Go code (after all, as gophers our aim is to write code that works and not fall for anything like other language folks do, haha just kidding).

But What is defer?

So if you look for the official definition it goes like: “defer schedules a function call to run right after the surrounding function returns“. Ok but what does this actually mean? In simple terms, this means that any function call preceded by defer is added to a stack of deferred calls and will be executed just before the parent function (where the defer is declared) exits.

The deferred calls are executed in Last-In, First-Out (LIFO) order.

So How Does defer Work?

Let’s take a code example which will help us understand this better and clear up what is the LIFO execution that we talked about earlier. So below is a simple Go code with some addition and some print statements. I think this is a pretty simple yet effective example to understand defer without buzzwords.

package main

import "fmt"

func main() {
    a := 54
    b := 34
    c := a + b
    fmt.Println(c)  // Prints the sum immediately
    defer fmt.Printf("from defer: %v\n", a + b)  
    defer fmt.Println("hello world from defer")
    fmt.Printf("Hello, World!\n")
}

Any guesses what will be the output? Let’s look at the output first:

88
Hello, World!
hello world from defer
from defer: 88

Why and How This Happens

Let’s analyze the execution flow of Go’s defer statement step by step.

Step-by-Step Execution

Immediate Execution:

  • Computes c = a + b88
  • Prints: 88

Deferring Function Calls:

defer fmt.Printf("from defer: %v\n", a + b)
defer fmt.Println("hello world from defer")

Arguments are evaluated immediately:

  • a + b88
  • "hello world from defer" is ready
  • Functions themselves are pushed onto a stack for later execution

Regular Execution Continues:

  • Prints: Hello, World!

Deferred Functions Execute in Reverse (LIFO):

  1. First, prints the last deferred call:
   hello world from defer
  1. Then, prints the first deferred call:
   from defer: 88

Complete Output

88
Hello, World!
hello world from defer
from defer: 88

Key Takeaways

  • Arguments to deferred functions are evaluated when defer is encountered
  • Deferred functions execute in Last-In-First-Out (LIFO) order
  • Deferred functions run just before the surrounding function returns

More Common Use Cases: File Handling

package main

import (
    "fmt"
    "os"
)

func main() {
    file, err := os.Open("example.txt")
    if err != nil {
        fmt.Println("Error opening file:", err)
        return
    }

    // Ensure the file gets closed when the function exits
    defer file.Close()

    fmt.Println("File opened successfully.")
}

👉 Here, file.Close() is guaranteed to run at the end of main(), even if the function returns early due to an error. This helps a lot in resource cleanup and making sure the file is closed after usage.

Ok, So We’ve Understood defer, Now Let’s Do Something Fun

One interesting trick with defer is reversing a string without using loops or extra arrays. Since deferred functions execute in Last-In, First-Out (LIFO) order, we can take advantage of this to print characters in reverse.

package main

import "fmt"

func main() {
    word := "golang"
    fmt.Print("Reversed word: ")

    for i := 0; i < len(word); i++ {
        ch := word[i] // capture the character immediately
        defer fmt.Printf("%c", ch)
    }
}

How It Works

  1. Each iteration of the loop defers printing a character
  2. Deferred calls are stacked, so they execute in reverse order when main() ends
  3. The result is the string printed backwards:
Reversed word: gnalog

Conclusion

I hope this post has helped you understand defer in an easy and fun way!

Happy coding in Go! 🐹


This content originally appeared on DEV Community and was authored by Abhishek Kumar