ProgrammingSenior Go Developer

How do defer functions work in Go: how are they called, in what order are they executed, and what nuances should be considered when using them?

Pass interviews with Hintsage AI assistant

Answer.

defer was introduced in Go to simplify resource management (e.g., files, mutexes, connections) — for any case where it is necessary to ensure that operations are executed at the very end of the function's execution. Historically, similar constructs existed in other languages (finally in Java, try-with-resources) but Go implements a more explicit and understandable pattern.

Problem: You always need to be sure that resources are released, even if an error occurs or a panic happens. Double resource closure or leaks are a common problem in classical programming style.

Solution: Everything declared with defer in a function or method is pushed onto a call stack and will be executed in reverse order before exiting the function. This guarantees resource release even in the face of exceptions (panic) or premature returns.

Example code:

func processFile() error { f, err := os.Open("filename.txt") if err != nil { return err } defer f.Close() // closing the file will happen at the end // work with the file return nil }

Key features:

  • Defer functions are always executed in LIFO (last in, first out) order — the last declared is called first
  • Arguments for defer are evaluated immediately, while the function itself is deferred
  • Even if the function exits due to panic, all defers will be called

Trick questions.

Will defers execute if a panic occurs within the function?

Yes! All defer functions will be called even in the case of panic, this is the main mechanism of "finalization".

When are the function arguments passed to defer evaluated?

At the time of the defer declaration, not when it is actually executed. Therefore, if using variables that change later, this should be taken into account:

a := 1 defer fmt.Println(a) a = 2 // will output 1, not 2

How does defer work inside a loop? Will this lead to memory leaks?

If defer is used in each iteration of the loop, all defers will only execute after the entire function completes, not after each iteration — the entire stack of defer functions will accumulate, which can lead to excessive memory consumption.

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

Common mistakes and anti-patterns

  • Using defer in loops, which causes delayed resource release (e.g., database connections)
  • Being completely confident that variables in defer are immutable — but their value is fixed immediately
  • Delayed release of heavy resources too late instead of manual calling

Real-life example

Negative case

Opening a thousand files in a loop, and using defer for each one. All files will only be closed at the end of the entire function, and resources will be held up, leading to a "leak" — exceeding the limit of open files.

Pros:

  • Concise notation
  • Guarantees release in any case

Cons:

  • Resource leaks until the entire function completes
  • Errors in mass operations with defer

Positive case

Using local functions in a loop, where defer is applied only for the scope of that file, not for the entire handler:

for _, name := range fileNames { func() { f, _ := os.Open(name) defer f.Close() // work with f }() }

Pros:

  • Immediate return of resources
  • No leaks

Cons:

  • Harder to read (additional function nesting)
  • Need to remember the scope of defer