Go > Core Go Basics > Functions > Defer statements

Understanding Defer Statements in Go

This example demonstrates the usage of the defer statement in Go, which schedules a function call to be executed after the surrounding function returns. We'll explore how defer works with multiple statements and how it interacts with function arguments.

Basic Defer Example

This code snippet shows the most basic use of defer. The fmt.Println("World!") statement is deferred until after fmt.Println("Hello,") has finished executing. Therefore, the output will be 'Hello,' followed by 'World!'. The defer keyword ensures the function call happens at the end of the enclosing function, regardless of how the function exits (normal return or panic).

package main

import "fmt"

func main() {
	defer fmt.Println("World!")
	fmt.Println("Hello,")
}

Defer Stack (LIFO)

Go executes deferred functions in Last-In-First-Out (LIFO) order. This means the last defer statement you write will be the first one executed when the surrounding function returns. In this example, the output will be 'Start', then 'One', then 'Two', then 'Three'.

package main

import "fmt"

func main() {
	defer fmt.Println("Three")
	defer fmt.Println("Two")
	defer fmt.Println("One")
	fmt.Println("Start")
}

Defer and Function Arguments

It's important to understand how defer interacts with function arguments. The arguments to the deferred function are evaluated at the time the defer statement is executed, not when the deferred function is actually called. In this example, the value of i is 1 when the defer statement is encountered. The `increment` function increases the value to 2. Therefore, the output will be 'Current value: 2' followed by 'Deferred value: 1'. The deferred function uses the value of `i` at the time it was deferred.

package main

import "fmt"

func increment(i int) int {
	i++
	return i
}

func main() {
	i := 1
	defer fmt.Println("Deferred value:", i)
	i = increment(i)
	fmt.Println("Current value:", i)
}

Real-Life Use Case: Closing Resources

A common use case for defer is to ensure that resources like files are closed properly, even if errors occur. In this example, file.Close() is deferred. This guarantees that the file will be closed when main() returns, regardless of whether an error occurred while opening or reading the file. This is crucial for preventing resource leaks.

package main

import (
	"fmt"
	"os"
)

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

	// Read from the file (implementation omitted for brevity)
	fmt.Println("File opened successfully.")
}

Best Practices

  • Use defer for cleanup tasks like closing files, unlocking mutexes, and closing network connections.
  • Be mindful of the LIFO order of deferred calls.
  • Understand how arguments are evaluated when a function call is deferred.
  • Avoid deferring expensive operations if possible, as they will delay the return of the function.

Interview Tip

Be prepared to explain how defer works, especially the LIFO ordering and the timing of argument evaluation. You might be asked to predict the output of code snippets involving defer.

When to Use Them

Use defer when you need to guarantee that a function will be executed regardless of how the enclosing function returns. This is especially important for resource management.

Alternatives

While defer is very convenient, you could manually place the cleanup code at the end of a function and in all error return paths. However, this is much more error-prone and less readable than using defer.

Pros

  • Ensures cleanup code is always executed.
  • Improves readability by placing cleanup code near the resource allocation.
  • Reduces the risk of resource leaks.

Cons

  • Can slightly increase the execution time of a function.
  • Overuse can make code harder to follow.

FAQ

  • What happens if a deferred function panics?

    If a deferred function panics, the panic will be caught by the Go runtime, and the remaining deferred functions will still be executed before the program crashes (if the panic isn't recovered).
  • Can I use defer inside a loop?

    Yes, you can use defer inside a loop. However, be aware that the deferred functions will accumulate and be executed when the loop's surrounding function returns. This might lead to unexpected behavior or resource exhaustion if not handled carefully.