Using loop iterator variable issues in Golang

Mohamed AbdEl Mohaimen
2 min readMar 16, 2021

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 []*File
for _, 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.

--

--