Historically, the defer concept was introduced in Go for safely releasing resources and finalizing actions regardless of how the function execution finishes (either normally or due to a panic). However, the interaction of defer with return and panic has several pitfalls that even experienced developers often overlook.
The issue is that the order of evaluation of return values, the behavior of named return values, and changing these values in defer is significantly different from the typical behavior in many languages. Furthermore, errors can occur if there is an attempt to change already computed values in defer, causing unexpected behavior.
The solution is to always remember: the values returned by a function are computed BEFORE the defer runs, but if named results are used, they can be changed inside defer before the actual return from the function.
Example code:
func tricky() (res int) { defer func() { res = 42 // Changes the return value! }() return 10 } func main() { fmt.Println(tricky()) // Will print 42, not 10 }
Key features:
In what order do deferred (defer) functions execute?
They execute strictly in reverse order of their declaration (stack — LIFO).
func f() { defer fmt.Println("1") defer fmt.Println("2") } // Will print: 2, then 1
When are the parameters for defer-functions evaluated — at the time of the defer declaration or at its execution?
Parameters for defer-functions are evaluated at the time of the defer declaration, not at the call time.
func f() { i := 1 defer fmt.Println(i) // will print 1, even if i changes later i = 2 }
Can defer change an unnamed result of the function?
No. Only named return values can be changed in defer.
func f() int { defer func() { /* cannot change anything */ }() return 5 }
A young developer wanted to log the return code in defer and mistakenly changed the named return value, thus "overwriting" the actual result of the function.
Pros:
Cons:
In another situation, defer was used solely for resource releasing, logging, and did not change the return, while important values were explicitly assigned before return.
Pros:
Cons: