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.