Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How golang's "defer" capture closure's parameter?

Tags:

closures

go

Here is my code (run):

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 } 

Output:

0 1 2 3 4 4 3 2 1 0 4 4 4 4 4

Question: What's the difference between part 2 & part 3? Why part 2 output "44444" instead of "43210"?

like image 416
Reck Hou Avatar asked Apr 15 '13 08:04

Reck Hou


People also ask

How does defer work in Golang?

In Golang, the defer keyword is used to delay the execution of a function or a statement until the nearby function returns. In simple words, defer will move the execution of the statement to the very end inside a function.

How do you defer in Golang?

In Go language, defer statements delay the execution of the function or method or an anonymous method until the nearby functions returns. In other words, defer function or method call arguments evaluate instantly, but they don't execute until the nearby functions returns.

Why does go have defer?

In the Go programming language, defer is a keyword that allows developers to delay the execution of a function until the current function returns. What throws some people off is that the deferred function's arguments are evaluated immediately, but the function itself doesn't fire until the wrapping function exits.

What is defer func ()?

A defer statement postpones the execution of a function until the surrounding function returns, either normally or through a panic. func main() { defer fmt.Println("World") fmt.Println("Hello") } Hello World.


2 Answers

The 'part 2' closure captures the variable 'i'. When the code in the closure (later) executes, the variable 'i' has the value which it had in the last iteration of the range statement, ie. '4'. Hence the

4 4 4 4 4 

part of the output.

The 'part 3' doesn't capture any outer variables in its closure. As the specs say:

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.

So each of the defered function calls has a different value of the 'n' parameter. It is the value of the 'i' variable in the moment the defer statement was executed. Hence the

4 3 2 1 0 

part of the output because:

... deferred calls are executed in LIFO order immediately before the surrounding function returns ...


The key point to note is that the 'f()' in 'defer f()' is not executed when the defer statement executes

but

the expression 'e' in 'defer f(e)' is evaluated when the defer statement executes.

like image 181
zzzz Avatar answered Nov 09 '22 12:11

zzzz


I would like to address another example in order to improve the understanding of defer mechanish, run this snippet as it is first, then switch order of the statements marked as (A) and (B), and see the result to yourself.

package main  import (     "fmt" )  type Component struct {     val int }  func (c Component) method() {     fmt.Println(c.val) }  func main() {     c := Component{}     defer c.method()  // statement (A)     c.val = 2 // statement (B) } 

I keep wonderng what are the correct keywords or concepts to apply here. It looks like that the expression c.method is evaluated, thus returning a function binded to the actual state of the component "c" (like taking an snapshot of the component's internal state). I guess the answer involves not only defer mechanish also how funtions with value or pointer receiver works. Do note that it also happens that if you change the func named method to be a pointer receiver the defer prints c.val as 2, not as 0.

like image 21
Victor Avatar answered Nov 09 '22 11:11

Victor