Go

In what order do deferred calls run, and what is wrong with using defer inside a loop?

Question 15MediumGo 1.22 to 1.25

Deferred calls are pushed onto a stack for each function call and run in LIFO order when the function returns, whether it returns normally or by panicking. A defer runs when the function ends, not when the enclosing block ends.

for i := range 3 {
	defer fmt.Print(i, " ")
}
// prints: 2 1 0

// BUG: every file stays open until processAll returns, so file descriptors can run out
func processAll(paths []string) error {
	for _, p := range paths {
		f, err := os.Open(p)
		if err != nil {
			return err
		}
		defer f.Close()
		// ... use f
	}
	return nil
}

// FIX: move the loop body into its own function so each defer runs per iteration
func processAll(paths []string) error {
	for _, p := range paths {
		if err := processOne(p); err != nil {
			return err
		}
	}
	return nil
}

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

Performance: since Go 1.14, "open-coded defers" make a simple defer almost free. This works for defers that are not in a loop, when a function has at most 8 of them. A defer inside a loop is never open-coded. It falls back to a heap-allocated defer record, which is noticeably slower. What the interviewer wants to hear: you know about resource leaks from defer-in-loop, and you know defer is scoped to the function.

More on Language Fundamentals & Types

All 39 Language Fundamentals & Types questions