Go

What does this print, and what is the bug in the second function?

Question 480MediumGo 1.22 to 1.25
func order() {
    for i := range 3 {
        defer fmt.Print(i, " ")
    }
    fmt.Print("end ")
}

func processAll(paths []string) error {
    for _, p := range paths {
        f, err := os.Open(p)
        if err != nil {
            return err
        }
        defer f.Close() // BUG
        // ... read f ...
    }
    return nil
}

order() prints end 2 1 0. Defers are pushed on a per-function LIFO stack and run when the function returns, not when the block or iteration ends.

The same fact causes the bug in processAll. Every file stays open until the function returns, so with 10,000 paths you can hit "too many open files". Fix it by moving the body into its own function so that each defer runs once per iteration:

for _, p := range paths {
    if err := processOne(p); err != nil {
        return err
    }
}

func processOne(p string) error {
    f, err := os.Open(p)
    if err != nil {
        return err
    }
    defer f.Close()
    // ... read f ...
    return nil
}

Defers also cost a little per call. Inside a hot loop they add up even when resources are not the problem.

More on Tricky Output & Code-Review Puzzles

All 38 Tricky Output & Code-Review Puzzles questions