Monday, September 25, 2023

Go End The Loop Bug

 


Finally, it is solved!

The Go maintainers have finally took the correct actions to fix the loop variable bug. This bug is very annoying, and I have personally made it several times.

Let's examine a simple example of this bug.


func main() {
var waitGroup sync.WaitGroup
for i := 0; i < 10; i++ {
waitGroup.Add(1)
go func() {
defer waitGroup.Done()
fmt.Printf("%v\n", i)
}()
}
waitGroup.Wait()
}


We would expect this simple loop to print the numbers zero to nine, but actually the result is unexpected:


10
10
10
4
10
10
10
10
10
10


Why? This is due to the fact that the scope of the loop variable `i` is the loop itself, so it is created only once, and referred by the iterations. To avoid this, we used to "clone" the loop variable, for example:


func main() {
var waitGroup sync.WaitGroup
for i := 0; i < 10; i++ {
waitGroup.Add(1)
clonedI := i
go func() {
defer waitGroup.Done()
fmt.Printf("%v\n", clonedI)
}()
}
waitGroup.Wait()
}


and the output:


8
9
2
3
0
5
4
1
7
6


So this works, but you always need to remember this. In the upcoming Go 1.22 version, the scope of the loop variable is changed into the iteration, so there is no longer need of the clone trick. We can even force it into Go version 1.21 using the environment variable: 

GOEXPERIMENT=loopvar


Good move GO! (better late than never)






No comments:

Post a Comment