Golang: Variables in range loop
Golang provides an interesting construct to range over arrays and maps. For instance to range over arrays,
type X struct {
n int
square int
}
func main() {
var xs [4]X
for i, x := range xs {
x.a, x.b = i, i*i
}
}
xs
is the array, i
is the index for each iteration and x
holds the value
for each iteration.
Some times while doing the above pattern of programming, especially coming
from other languages, it is easy to get confused. For instance, let us say we
want to build a map
out of an array by ranging over them,
func main() {
var xs [4]X
ym := make(map[*X]int) // the map we need to build
for i, x := range xs {
ym[&x] = i*i*i
fmt.Printf("%p %p\n", &x, &xs[i])
}
fmt.Println("Map output")
fmt.Println(ym)
}
Above program, when executed, emits following output
0x2101fb150 0x210230040
0x2101fb150 0x210230050
0x2101fb150 0x210230060
0x2101fb150 0x210230070
map[0x2101fb150:100]
Why does the map contain just one key ? Why is the address of x
in each
iteration is the same. The common type of misconception that leads to this
confusion is the way we understand how x
is declared and reused. The
misconception can be explained by expanding the range loop into traditional
for-loop,
ym := make(map[*X]int) // the map we need to build
for i := 0; i < len(xs); i++ {
x := xs[i]
...
}
Which is wrong, the correct way of expanding the range loop, in relation to how go would compile this code:
ym := make(map[*X]int) // the map we need to build
var x X
for i := 0; i < len(xs); i++ {
x = xs[i]
...
}
In the former case, x is declared and instantiated within the scope of the for loop. In the later case x is declared and instantiated just once outside the for-loop.