package main
import (
\"fmt\"
\"time\"
)
type field struct {
name string
}
func (p *field) print() {
fmt.Println(p.name)
}
func main() {
There is a data race.
The code implicitly takes address of variable v
when evaluating arguments to the goroutine function. Note that the call v.print()
is shorthand for the call (&v).print().
The loop changes the value of variable v
.
When goroutines execute, it so happens that v
has the last value of the loop. That's not guaranteed. It could execute as you expected.
It's helpful and easy to run programs with the race detector. This data race is detected and reported by the detector.
One fix is to create another variable scoped to the inside of the loop:
for _, v := range data {
v := v // short variable declaration of new variable `v`.
go v.print()
}
With this change, the address of the inner variable v
is taken when evaluating the arguments to the goroutine. There is a unique inner variable v
for each iteration of the loop.
Yet another way to fix the problem is use a slice of pointers:
data := []*field{ {"one"},{"two"},{"three"} } // note '*'
for _, v := range data {
go v.print()
}
With this change, the individual pointers in the slice are passed to the goroutine, not the address of the range variable v
.
Another fix is to use the address of the slice element:
data := []field{ {"one"},{"two"},{"three"} } // note '*'
for i:= range data {
v := &data[i]
go v.print()
}
Because pointer values are typically used with types having a pointer receiver, this subtle issue does not come up often in practice. Because field
has a pointer receiver, it would be typical to use []*field
instead of []field
for the type of data
in the question.
If the goroutine function is in an anonymous function, then a common approach for avoiding the issue is to pass the range variables as an argument to the anonymous function:
for _, v := range data {
go func(v field) {
v.print() // take address of argument v, not range variable v.
}(v)
}
Because the code in the question does not already use an anonymous function for the goroutine, the first approach used in this answer is simpler.