thecodex.expert · The Codex Family of Knowledge
Go

Functions and defer

Functions can return more than one value with no wrapper object needed, and defer guarantees cleanup code runs no matter how the function exits.

Go 1.26 defer is LIFO Last verified:
Canonical Definition

Go functions natively support multiple return values — no tuple type or wrapper object required — which is how the idiomatic value, err := doThing() pattern works. The defer statement schedules a function call to run immediately before the enclosing function returns, regardless of which return statement fired or whether a panic occurred; multiple deferred calls run in last-in-first-out (LIFO) order.

Multiple return values

Any Go function can return more than one value, declared in the signature as a comma-separated list. This is the mechanism behind Go's error-handling convention: a function that can fail returns its normal result alongside an error, and the caller checks the error explicitly rather than catching an exception.

Gofunctions.go
func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, fmt.Errorf("cannot divide %v by zero", a)
    }
    return a / b, nil
}

result, err := divide(10, 2)
if err != nil {
    log.Fatal(err)
}
fmt.Println(result)   // 5

Named return values

A function's return values can be given names in the signature, which pre-declares them as local variables initialized to their zero value. A bare return with no arguments then returns whatever those named variables currently hold — useful for short functions but easy to make confusing in long ones, so idiomatic Go uses named returns sparingly.

Gonamed_returns.go
func split(sum int) (x, y int) {
    x = sum * 4 / 9
    y = sum - x
    return   // returns x and y automatically
}

defer: guaranteed cleanup, LIFO order

A defer call is scheduled to run right before its enclosing function returns — whether that's via a normal return, an early return, or a panic unwinding the stack. This makes it Go's idiomatic way to guarantee cleanup (closing a file, unlocking a mutex, closing a database connection) happens exactly once, right next to where the resource was acquired. Multiple defers in the same function run in last-in-first-out order: the most recently deferred call runs first.

Godefer.go
func readFile(path string) error {
    f, err := os.Open(path)
    if err != nil {
        return err
    }
    defer f.Close()   // guaranteed to run when readFile returns, however it returns

    // ... use f ...
    return nil
}

func order() {
    defer fmt.Println("1")
    defer fmt.Println("2")
    defer fmt.Println("3")
}
// order() prints: 3, 2, 1 — LIFO

Sources

1
The Go Authors. The Go Programming Language Specification, "Defer statements", go.dev/ref/spec#Defer_statements.