Today I Learned

A Hashrocket project

Captured for var in closure can change

Variables can be captured into anonymous functions in go with closures, and its easy to use the variables we declare in a for loop in those anonymous functions, but that doesn’t produce the results I would expect.

var fnArray []func()

for _, chr := range []string{"a", "b", "c"} {
    fmt.Println("Hello,", chr)
    fn := func() {
      fmt.Println("Goodbye,", chr)
    }
    fnArray = append(fnArray, fn)
}

for _, fn := range fnArray {
  fn()
}

The above code outputs:

Hello,a
Hello,b
Hello,c
Goodbye,c
Goodbye,c
Goodbye,c

Go optimizes for loops by using the same memory space for each value that it terates over. To avoid this hard edge you can reassign that value:

capturedChr := chr

Inserted that assignment into our for loop looks like this:

var fnArray []func()

for _, chr := range []string{"a", "b", "c"} {
    capturedChr := chr
    fmt.Println("Hello,", chr)
    fn := func() {
      fmt.Println("Goodbye,", capturedChr)
    }
    fnArray = append(fnArray, fn)
}

for _, fn := range fnArray {
  fn()
}

And produces this more correct output:

Hello,a
Hello,b
Hello,c
Goodbye,a
Goodbye,b
Goodbye,c

Checkout the go playground here