Usage of reference in "for range loops" leads to a bug

Got pinched by a bug in the for range loop in my code. Let me describe it, so others can avoid it.

Principle: Avoid taking a reference of the local variable in the for range, and store it.

Inside the for range it is to keep in mind that if you are taking the reference of the assigned variable and storing it in another structure. It can give unexpected results.

package main


import "fmt"

type Container struct {
    val *int
}

func printContainer(cs []Container) {
  for _, c := range cs {
     fmt.Printf("%d\n", *c.val)
  }
}

func incorrectWay(nums []int) {
  cs := []Container{}
  for _, num := range nums {
    c := Container{
      val: &num,
    }
    cs = append(cs, c)
  }
  printContainer(cs)
}

func correctWay(nums []int) {
  cs := []Container{}
  for i, _ := range nums {
    c := Container{
      val: &nums[i],
    }
    cs = append(cs, c)
  }
  printContainer(cs)
}

func main() {
  nums := []int{1,2,3}
  fmt.Println("Undesired Result")
  incorrectWay(nums)
  fmt.Println("Desired Result")
  correctWay(nums)
}

Output for this one comes as:

Undesired Result
3
3
3
Desired Result
1
2
3

Go Play link: https://play.golang.org/p/hLFP2qyrfIj

In the function incorrectWay container stores a reference for the local variable num which contains a copy of the nums[i]. In the iterations of the loop value of the num keeps on changing but the reference remains the same. So at the end in the memory location where num lives only last value of the array remains and that is what gets printed.

To get to the desired result, do similar to what has been done in function correctWay. Create an index i and use that to get the corresponding element in nums and use that as the reference for storing in the container.

comments powered by Disqus