Without i := i, I get an incorrect result (3, 3, 5, 9, 7, 15). With it, I get (0, 0, 3, 3, 6, 10), which is correct. Removing the assignment is similar to getting the value of i at the end of the loop. Why?
package main
import "fmt"
type Handler interface {
Handle(v int)
}
type Elem struct {
Handler Handler
}
var elems []*Elem
type handlerFunc func(v int)
func (h handlerFunc) Handle(v int) { h(v) }
func main() {
newElem := func(fn handlerFunc) {
elem := &Elem{Handler: handlerFunc(fn)}
elems = append(elems, elem)
}
for i := 0; i < 3; i++ {
i := i // *** Why? ***
newElem(func(v int) { fmt.Printf("%d, ", i+v) })
newElem(func(v int) { fmt.Printf("%d, ", i*v) })
}
for n, e := range elems {
if e.Handler != nil {
e.Handler.Handle(n)
}
}
fmt.Printf("
")
}
The easiest way to visualize what is going on is to change the output of your functions to:
newElem(func(v int) { fmt.Printf("plus %d - %d+%d
", i+v, i, v) })
newElem(func(v int) { fmt.Printf("times %d - %d*%d
", i*v, i , v) })
With this change the output becomes:
plus 3 - 3+0
times 3 - 3*1
plus 5 - 3+2
times 9 - 3*3
plus 7 - 3+4
times 15 - 3*5
So, as you can see, i
is 3 in all cases. This is because you are creating closures around the i
variable, so the functions will use the current value of i
when they run, which is 3 by the time the functions actually use i
.
You can again see this if you change your code like the following:
http://play.golang.org/p/FRhr0n2oi7
The reason the assignment i := i
inside your loop fixes the problem is because you are creating a new variable i
inside the scope of your loop which is still closed over by the functions, but doesn't ever change. Each new iteration of the loop creates a new i
so no previous i
changes value.
Although this document describes a common mistake when dealing with goroutines and closures, it should shed a bit more light on the issue and potential solutions.