◎ Go for
Java Developers
Section 09

Defer

defer schedules a function call to run when the surrounding function returns — whether it returns normally, returns early via any path, or panics. It replaces try/finally.

defer vs try-finally

☕ Java — try-finally for cleanup
ResultSet rs = null;
try {
    rs = db.query("SELECT ...");
    // ... process ...
    return result;
} finally {
    // runs even if exception thrown
    if (rs != null) rs.close();
}
◎ Go — defer for cleanup
rows, err := db.Query("SELECT ...")
if err != nil {
    return nil, err
}
// Register cleanup RIGHT AFTER acquiring the resource
// Much harder to forget than a finally block
defer rows.Close()  // runs when queryMessage() returns

// ... process rows ...
return result, nil  // rows.Close() runs here
// Any early return above also triggers rows.Close()
Why Go does thisdefer collocates the cleanup with the acquisition. In Java, the cleanup lives in finally which may be 50 lines away. With defer, rows.Close() is on the line after rows, err := db.Query() — impossible to forget, impossible to miss in a code review.

LIFO — multiple defers stack

defers run in reverse order of registration
func openResources() {
    db := openDatabase()      // step 1: open database
    defer db.Close()          // registered 1st — runs LAST

    conn := db.NewConn()      // step 2: open connection (depends on db)
    defer conn.Close()        // registered 2nd — runs 2nd

    tx, _ := conn.Begin()     // step 3: begin transaction (depends on conn)
    defer tx.Rollback()       // registered 3rd — runs FIRST
    // If tx.Commit() succeeds, Rollback() is a safe no-op

    // ... do work ...
    tx.Commit()
}
// Cleanup order: tx.Rollback(), conn.Close(), db.Close()
// = exact mirror of open order — each resource closed before its dependency
LIFO mirrors the open order. Resources opened last (most dependent on others) are closed first. This is the correct order for safe cleanup.

Argument capture — evaluated at defer time

defer arguments are snapshotted when defer is registered
i := 0
defer fmt.Println(i)  // i evaluated NOW = 0
i = 100
// Function returns — deferred Println prints 0, not 100
// The argument was captured at the defer line

// To capture the FINAL value, use a closure (no arguments):
j := 0
defer func() {
    fmt.Println(j)  // j read at exit time — prints 100
}()
j = 100

Tracing pattern — enter/exit timing

single-line function tracing with defer
func trace(name string) func() {
    start := time.Now()
    fmt.Printf(">>> enter %s\n", name)
    // Returns a cleanup function
    return func() {
        fmt.Printf("<<< exit  %s  (%v)\n", name, time.Since(start))
    }
}

func (e *Engine) RouteMessage(msg Message) error {
    defer trace("RouteMessage")()  // two calls: trace() then defer on result
    //          ^^^^^^^^^^^^^^^^  trace() runs NOW — prints '>>> enter'
    //                          ^^ returned func is deferred — prints '<<< exit'

    return e.store.Save(msg)
}

Panic and recover

☕ Java — RuntimeException unwinds stack
// Unchecked exception unwinds the stack
void riskyOp() {
    throw new RuntimeException("something broke");
}

try {
    riskyOp();
} catch (RuntimeException e) {
    // caught here
}
◎ Go — panic unwinds, recover catches
// panic unwinds the stack, running deferred functions
func riskyOp() {
    panic("something broke")
}

// recover() inside a deferred function catches a panic
func safeOp() (err error) {
    defer func() {
        if r := recover(); r != nil {
            // convert panic to error — goroutine survives
            err = fmt.Errorf("recovered panic: %v", r)
        }
    }()
    riskyOp()  // panics — recover catches it above
    return nil
}
Why Go does thisPanic is not for normal error handling — it is for truly unrecoverable situations (nil pointer, out of bounds, programmer error). Use it at startup for missing required config. In production handlers, recover prevents one bad request from crashing the server.
The full defer + mutex and defer + channel patterns are in section 13 — Defer Advanced — after those primitives are introduced.