Go

How can a deferred function change a function's return value? What do f() and g() return?

Question 16HardGo 1.22 to 1.25
func f() (n int) {
	defer func() { n *= 2 }()
	return 3
}

func g() int {
	n := 3
	defer func() { n *= 2 }()
	return n
}

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

Answer: f() returns 6, g() returns 3. safeDiv(1, 0) returns 0, recovered: runtime error: integer divide by zero.

return X is not a single step. First it assigns X to the result parameters. Then the deferred functions run. Then the function actually returns. With named results, a deferred closure can read and change the result after the return statement has set it. In g, the result has no name. The value of n is copied into a hidden result slot, so changing the local n afterwards has no effect.

Common real-world uses:

  • Turning a panic into an error, as in safeDiv.
  • Wrapping errors, e.g. defer func(){ if err != nil { err = fmt.Errorf("op: %w", err) } }().
  • Keeping a Close error: defer func(){ if cerr := f.Close(); err == nil { err = cerr } }(). Better still, use errors.Join(err, cerr).

More on Language Fundamentals & Types

All 39 Language Fundamentals & Types questions