Go Gopher How to Go

Defer is a keyword in Go that schedules a function call to be executed after the surrounding function returns. It's commonly used for cleanup tasks like closing files, unlocking mutexes, or releasing resources.

💡 Key Points

  • Deferred functions execute in LIFO (Last In, First Out) order
  • Arguments to deferred functions are evaluated immediately
  • Deferred functions run even if a panic occurs
  • Defer is commonly used for resource cleanup
  • Multiple defers stack up - last deferred runs first
  • Deferred functions can modify named return values
  • Defer has a small performance cost - use judiciously in hot paths

Basic Defer Usage

Defer schedules a function call to run after the current function completes:

package main

import "fmt"

func main() {
    defer fmt.Println("world")
    fmt.Println("hello")
}
Output:
hello
world

Defer Execution Order

Multiple deferred calls are executed in reverse order (LIFO):

package main

import "fmt"

func main() {
    fmt.Println("counting")
    
    for i := 0; i < 3; i++ {
        defer fmt.Println(i)
    }
    
    fmt.Println("done")
}
Output:
counting
done
2
1
0

Defer with File Handling

One of the most common uses of defer is closing files and resources:

package main

import (
    "fmt"
    "os"
)

func main() {
    f, err := os.Create("test.txt")
    if err != nil {
        fmt.Println("error:", err)
        return
    }
    defer f.Close()
    
    // Write to file
    f.WriteString("Hello, defer!")
    fmt.Println("file written successfully")
    
    // File automatically closed when function returns
}
Output:
file written successfully

Defer Behavior

AspectBehaviorExample
Execution OrderLIFO (Last In, First Out)Last defer executes first
Argument EvaluationArguments evaluated immediatelydefer fmt.Println(i) captures current i
Execution TimingAfter return statement, before return to callerCan modify named return values
Panic HandlingDeferred functions still run during panicUsed for cleanup in error scenarios
Return ValuesCan modify named return valuesUseful for error handling patterns
ScopeFunction-level, not block-levelDefers in loops accumulate

Common Defer Patterns

PatternCodeUse Case
File Closingdefer file.Close()Ensure files are closed
Mutex Unlockingmu.Lock(); defer mu.Unlock()Ensure locks are released
Connection Closingdefer conn.Close()Close database/network connections
Timing Functionsdefer timeTrack(time.Now())Measure function execution time
Cleanup Resourcesdefer cleanup()General resource cleanup
Recover from Panicdefer func() { recover() }()Catch and handle panics

Defer Gotchas

IssueProblemSolution
Loop Variablesfor _, f := range files { defer f.Close() }Defers accumulate, close immediately or use closure
Argument Evaluationdefer fmt.Println(x) captures x nowUse closure if you need final value: defer func() { fmt.Println(x) }()
Return Value ShadowingUnnamed returns not modifiableUse named return values if defer needs to modify
PerformanceSmall overhead per deferAvoid in tight loops or hot paths
Error Handlingdefer f.Close() ignores errorsCheck errors: defer func() { err = f.Close() }()

Advanced Defer Examples

TechniqueExampleDescription
Modify Return Valuefunc() (err error) { defer func() { if err != nil { err = wrap(err) } }(); ... }Wrap errors on return
Function Timingdefer func(t time.Time) { log.Println(time.Since(t)) }(time.Now())Log execution duration
Transaction Rollbackdefer func() { if err != nil { tx.Rollback() } else { tx.Commit() } }()Conditional cleanup based on error
Panic Recoverydefer func() { if r := recover(); r != nil { log.Println(r) } }()Gracefully handle panics
Stack Tracingdefer log.Println("exiting function")Track function entry/exit
Cleanup Chaindefer cleanup1(); defer cleanup2(); defer cleanup3()Multiple cleanup steps in order