>_
GolangStepByStep
Intern

Defer

Schedule cleanup code to run when a function exits

# Why defer Matters

defer guarantees cleanup when a function exits. This is critical in real code where multiple return paths exist.

It is commonly used to close files, unlock mutexes, stop timers, finish tracing, and release resources safely even when errors happen early.

Real-World Analogyclick to expand

Think of defer like checking out a rental car with a pre-scheduled return step. No matter how the trip ends, the return step still runs.

In services, this prevents leaks: open file handles, locked mutexes, and dangling transactions.

# Guaranteed Cleanup Pattern

func readFile(path string) error {
    f, err := os.Open(path)
    if err != nil {
        return err
    }
    defer f.Close() // always runs

    // ... read from f ...
    return nil
}

# LIFO Order (Stack Behavior)

Deferred calls execute in reverse order of registration.

func main() {
    fmt.Println("start")
    defer fmt.Println("first defer")
    defer fmt.Println("second defer")
    defer fmt.Println("third defer")
    fmt.Println("end")
}

// start
// end
// third defer
// second defer
// first defer

# Step-by-Step Evaluation Rules

Arguments are evaluated immediately

defer fmt.Println(x) stores x at the defer line, not at function exit.

Closures evaluate captured vars later

defer func() { fmt.Println(x) }() reads x when deferred function runs.

Named returns can be modified by defer

Deferred closures can wrap or replace named return values before caller receives them.

# Argument vs Closure Example

x := 1
defer fmt.Println("arg:", x)
defer func() { fmt.Println("closure:", x) }()

x = 99

// closure: 99
// arg: 1

# Real-World Example: timedRun Wrapper

Use defer to measure function execution time cleanly.

package main

import (
    "fmt"
    "time"
)

func timedRun(label string, fn func()) {
    start := time.Now()
    defer func() {
        fmt.Printf("%s took %v
", label, time.Since(start))
    }()
    fn()
}

func main() {
    timedRun("sleep", func() {
        time.Sleep(50 * time.Millisecond)
    })
}

⚡ Key Takeaways

  • defer guarantees cleanup on function exit
  • Deferred calls run in LIFO order
  • Deferred call arguments are evaluated immediately
  • Deferred closures can observe later state and modify named returns
  • Avoid heavy defer usage inside hot loops; prefer helper functions
practice & review