Tip:
You can use the "get address operator" & to confirm whether or not variables are the same.
Let's slightly modify your program to help our understanding.
package main
import "fmt"
import "time"
func main() {
for _, s := range []string{"foo", "bar"} {
x := s
fmt.Println(" &s =", &s, "\t&x =", &x)
func() {
fmt.Println("-", "&s =", &s, "\t&x =", &x)
fmt.Println("s =", s, ", x =", x)
}()
}
fmt.Println("\n\n")
for _, s := range []string{"foo", "bar"} {
x := s
fmt.Println(" &s =", &s, "\t&x =", &x)
go func() {
fmt.Println("-", "&s =", &s, "\t&x =", &x)
fmt.Println("s =", s, ", x =", x)
}()
}
time.Sleep(time.Second)
}
The output is:
&s = 0x1040a120 &x = 0x1040a128
- &s = 0x1040a120 &x = 0x1040a128
s = foo , x = foo
&s = 0x1040a120 &x = 0x1040a180
- &s = 0x1040a120 &x = 0x1040a180
s = bar , x = bar
&s = 0x1040a1d8 &x = 0x1040a1e0
&s = 0x1040a1d8 &x = 0x1040a1f8
- &s = 0x1040a1d8 &x = 0x1040a1e0
s = bar , x = foo
- &s = 0x1040a1d8 &x = 0x1040a1f8
s = bar , x = bar
Key points:
- The variable
s
in each iteration of the loop is the same variable.
- The local variable
x
in each iteration of the loop are different variables, they just happen to have the same name x
- In the first for loop, the
func () {} ()
part got executed in each iteration and the loop only continue to its next iteration after func () {} ()
completed.
- In the second for loop (goroutine version), the
go func () {} ()
statement itself completed instantaneously. When the statements in the func body got executed is determined by the Go scheduler. But when they (the statements in the func body) starts to execute, the for loop already completed! And the variable s
is the last element in the slice which is bar
. That's why we got two "bar"s in the second for loop output.
go func(s string) { ... }(s)
idiom. Another way of putting the issue is that Go scoping rules mean thefunc
s in both your examples are accessing whatever the current value ofs
is when they run; the goroutine just runs at a different time. – Endolymphgo vet
can detect this problem, go.dev/doc/faq#closures_and_goroutines – Corroborate