# Defer, Panic and Recover

> Source: https://learn-go.org/defer-panic-recover/
> Part of Learn Go, free to read.

## defer

`defer` schedules a call to run when the surrounding function returns — by any path, including a panic.

```go
func readConfig(path string) ([]byte, error) {
    f, err := os.Open(path)
    if err != nil {
        return nil, fmt.Errorf("open %s: %w", path, err)
    }
    defer f.Close()          // runs whichever return we take
    return io.ReadAll(f)
}
```

The cleanup sits next to the acquisition, so you cannot forget it three returns later. That pairing is the whole point.

Deferred calls run **last in, first out**:

```go
for i := 0; i < 3; i++ {
    defer fmt.Println(i)
}
// prints 2, 1, 0
```

### Arguments are evaluated immediately

The single most common `defer` surprise:

```go
i := 0
defer fmt.Println(i)     // captures 0 NOW
i = 42
// prints 0, not 42
```

The call is deferred; the arguments are not. To defer the evaluation too, wrap it in a closure:

```go
i := 0
defer func() { fmt.Println(i) }()    // reads i when it runs
i = 42
// prints 42
```

### defer in a loop leaks

```go
for _, path := range paths {
    f, _ := os.Open(path)
    defer f.Close()        // accumulates until the FUNCTION returns
}
```

With ten thousand paths you hold ten thousand descriptors. Move the body into its own function so each iteration gets its own scope:

```go
for _, path := range paths {
    if err := process(path); err != nil {
        return err
    }
}

func process(path string) error {
    f, err := os.Open(path)
    if err != nil { return err }
    defer f.Close()        // now scoped to one iteration
    return doWork(f)
}
```

### Capturing an error from a deferred close

`defer f.Close()` discards the close error, which for a file you *wrote* to is the error that tells you the write failed. Use a named return:

```go
func writeFile(path string, data []byte) (err error) {
    f, err := os.Create(path)
    if err != nil {
        return err
    }
    defer func() {
        if cerr := f.Close(); cerr != nil && err == nil {
            err = cerr          // only overwrite if there was no earlier error
        }
    }()
    _, err = f.Write(data)
    return err
}
```

A deferred closure can modify named return values, which is the mechanism that makes this work.

## panic

`panic` stops normal execution, runs deferred functions up the stack, and crashes the program with a stack trace.

```go
panic("unreachable state")
```

Some panics come from the runtime rather than your code:

```go
var p *User
p.Name                       // nil pointer dereference
s := []int{1, 2, 3}
s[10]                        // index out of range
var m map[string]int
m["key"] = 1                 // assignment to nil map
x / 0                        // integer divide by zero
```

**Do not use panic for ordinary errors.** Go's convention is to return an `error`, and a library that panics on bad input is a library nobody can use safely. Panic is for situations where continuing is meaningless:

```go
var tmpl = template.Must(template.ParseFiles("index.html"))
// startup, unrecoverable, fail loudly and immediately
```

The `Must` prefix is the convention for "this panics rather than returning an error", and it is reserved for package-level initialisation.

## recover

`recover` stops a panic, but only inside a deferred function:

```go
func safeDivide(a, b int) (result int, err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("recovered: %v", r)
        }
    }()
    return a / b, nil
}
```

There is essentially one legitimate use: **a boundary that must not let one unit of work take down the process.**

```go
func Middleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        defer func() {
            if rec := recover(); rec != nil {
                slog.Error("panic in handler",
                    "err", rec, "stack", string(debug.Stack()), "path", r.URL.Path)
                http.Error(w, "internal server error", http.StatusInternalServerError)
            }
        }()
        next.ServeHTTP(w, r)
    })
}
```

Note what it does: logs the panic **with the stack**, returns a generic 500, and lets the server keep serving other requests. It does not pretend nothing happened — a recovered panic is a bug and the log line is how you find it.

:::warn recover does not cross goroutines
```go
go func() {
    panic("boom")        // this kills the WHOLE program
}()
```
A panic in a goroutine cannot be recovered by its parent. Every goroutine that might panic needs its own deferred `recover`, which is a strong argument for a small helper that wraps goroutine launches in one.
:::

## Exercise

```go
package main

import "fmt"

func main() {
	// 1. Write process(paths []string) that opens each path and closes it
	//    correctly WITHOUT accumulating defers.
	// 2. Write safeRun(fn func()) error that runs fn and converts any panic
	//    into a returned error.
	fmt.Println("start")
}
```

## Common questions

### Is `defer` expensive?

Not meaningfully in modern Go — it is close to free in the common case. The place to think about it is inside a loop, where deferred calls accumulate until the function returns rather than the iteration ending.

### When is `panic` appropriate in my own code?

At startup for something unrecoverable — a template that will not parse, a required config that is absent — and for genuinely impossible states that indicate a programming error. Never for input validation or an I/O failure; those are `error` values.

### Should I put a `recover` in every handler?

One at the server boundary, as middleware, is right. Sprinkling `recover` through business logic hides bugs and makes debugging much harder — the panic will still be a bug, and you want it visible in your logs rather than silently swallowed mid-stack.
