Deferred Function Calls and Closure Capture
In Go, the defer statement allows us to execute a function just before the surrounding function returns. One important aspect of defer is how it handles closures.
In this code snippet:
package main import "fmt" func main() { var whatever [5]struct{} for i := range whatever { fmt.Println(i) } // part 1 for i := range whatever { defer func() { fmt.Println(i) }() // part 2 } for i := range whatever { defer func(n int) { fmt.Println(n) }(i) // part 3 } }
Part 1 simply prints the values of i from 0 to 4. Part 2, however, demonstrates an interesting behavior. Instead of printing the expected values of 0 to 4, it outputs "44444."
This is because the closure in part 2 captures the i variable. When the closure is executed later, the i variable has the value it had in the last iteration of the range statement, which is 4. As a result, all the deferred function calls print 4.
In contrast, part 3 does not capture any outer variables. According to the Go specifications, "each time the 'defer' statement executes, the function value and parameters to the call are evaluated as usual and saved anew but the actual function is not invoked." This means that each of the deferred function calls has a different value of the 'n' parameter, which is the value of 'i' at the time the 'defer' statement was executed.
Therefore, part 3 correctly prints "43210" as the deferred function calls are executed in LIFO order (last-in, first-out) before the surrounding function returns.
It's crucial to remember that the function expression in 'defer f()' is not executed at the time the defer statement executes, while the expression in 'defer f(e)' is evaluated immediately.
The above is the detailed content of Why does `defer func() { fmt.Println(i) }()` print '44444' in Go, while `defer func(n int) { fmt.Println(n) }(i)` correctly prints '43210'?. For more information, please follow other related articles on the PHP Chinese website!