June 27, 2022
In this series, I will cover interesting aspects of Go in a relatively brief manner.
Can you figure out what is wrong with the following program, which tries to print the keys and values of a map concurrently?
m := map[int]int{
0: 0,
1: 1,
2: 2,
3: 3,
}
for k, v := range m {
go func() {
fmt.Println(k, v)
}()
}
The behavior of the above program is undefined, as it will likely lead to data
races. In Go, loop variables (k and v in this case) are reused in each
iteration. The function literal, which is a closure, encloses them, causing all
the created goroutines to see the "same" variables: the loop variables k and
v. To fix this, copy the loop variables, like so:
for k, v := range m {
k, v := k, v // This is idiomatic Go.
go func() {
fmt.Println(k, v)
}()
}
Note that this only appertains to closures. If you use go fmt.Println(k, v)
instead, you do not have to worry—k and v are evaluated before the launch of
the goroutine(s), and a copy is passed to the function.
Tip: Use the -race flag with go run or go build to detect data races in
your program:
$ go run -race reuse_loop_var.go
0 0
0 0
==================
WARNING: DATA RACE
Read at 0x00c000138018 by goroutine 7:
main.main.func1()
/home/ab/src/playground/reuse_loop_var.go:19 +0x52
Previous write at 0x00c000138018 by main goroutine:
main.main()
/home/ab/src/playground/reuse_loop_var.go:16 +0x28e
Goroutine 7 (running) created at:
main.main()
/home/ab/src/playground/reuse_loop_var.go:18 +0x215
==================
==================
WARNING: DATA RACE0 0
Read at 0x00c000138028 by goroutine 8:
main.main.func1()
/home/ab/src/playground/reuse_loop_var.go:19 +0x84
Previous write at 0x00c000138028 by main goroutine:
main.main()
/home/ab/src/playground/reuse_loop_var.go:16 +0x2ab
Goroutine 8 (running) created at:
main.main()
/home/ab/src/playground/reuse_loop_var.go:18 +0x215
==================
3 0
Found 2 data race(s)
exit status 66