Using loop iterator variable issues in Golang
In Go, the loop iterator variable is a single variable that takes different values in each loop iteration. but when used wrong it leads to unintended behavior.
We are going to discuss two common mistakes.
Using reference to this variable:
When you use reference to the loop iterator variable, you will find that all values are the same because it references to just one variable address (the last updated one) For example:
func main() {var nums []*int
for i := 0; i < 3; i++ {
nums = append(nums, &i)
}fmt.Println("Unexpected result")
fmt.Println(*nums[0], *nums[1], *nums[2])}
It will output unexpected results:
3 3 3
One of the possible solutions is to copy the loop variable into a new variable:
func main() {var nums []*int
for i := 0; i < 3; i++ {
j:= i nums = append(nums, &j)
}fmt.Println("expected result")
fmt.Println(*nums[0], *nums[1], *nums[2])}
It will output expected results:
0 1 2
You can try it in the Go Playground
This issue also exist when using range over list of objects
when you use the reference of the iterator variable when loop over list of objects using range the same issue happens For example:
func main() {files := []File{{"333"}, {"555"}, {"888"}}
var filesCopy []*Filefor _, file := range files {
filesCopy = append(filesCopy, &file)
} fmt.Println("Unexpected result") for i := range filesCopy {
fmt.Println(filesCopy[i].ID)
}}
It will output unexpected results:
888 888 888
One of the possible solutions is to reference to the slice objects itself:
func main() {files := []File{{"333"}, {"555"}, {"888"}}
var filesCopy []*File
for i, _:= range files {
filesCopy = append(filesCopy, &files[i])
}
fmt.Println("expected result")
for i := range filesCopy {
fmt.Println(filesCopy[i].ID)
}}
It will output expected results:
333 555 888
You can try it in the Go Playground
Using it in goroutines
You have to avoid passing the loop iterator variable to closure like that:
for _, val := range values {
go func() {
fmt.Println(val)
}()}
It will always has the same value (the last one) you have to pass it as a parameter to the closure.
for _, val := range values {
go func(val string) {
fmt.Println(val)
}(val)
}
Finally we finished, i hope this article help you to avoid those issues.