range clause and the address of iteration variable
package mainimport (
"fmt"
)type T struct {
id int
}func main() {
t1 := T{id: 1}
t2 := T{id: 2}
ts1 := []T{t1, t2}
ts2 := []*T{}
for _, t := range ts1 {
ts2 = append(ts2, &t)
}
for _, t := range ts2 {
fmt.Println((*t).id)
}
}
Think for a moment about expected output of this program..
For some (including me) the result might be surprising at first glance:
2
2
I’ve personally expected
1
2
but it turned out to be wrong answer.
Iteration variable t is declared using short variable declaration. Its scope is the block of the “for” statement. It’s a variable which during first iteration hold value of the first element and during 2nd iteration value of the second element . But it’s just another place in memory which hold current element of slice being iterated over. It doesn’t point to values stored inside slice’s underlying array — it’s a temporary bucket where next elements are copied over. So &t
will have the same value in each iteration since it’s a auxiliary variable to hold currently iterated element.
t1 := T{id: 1}
t2 := T{id: 2}
ts1 := []T{t1, t2}
ts2 := []*T{}
for _, t := range ts1 {
t.id = 3
ts2 = append(ts2, &t)
}
for _, t := range ts2 {
fmt.Println((*t).id)
}
fmt.Println(ts1)
fmt.Println(t1)
fmt.Println(t2)
output:
3
3
[{1} {2}]
{1}
{2}
Possible solution is to use index and get the address of slice’s element (source code):
t1 := T{id: 1}
t2 := T{id: 2}
ts1 := []T{t1, t2}
ts2 := []*T{}
for i, _ := range ts1 {
ts2 = append(ts2, &ts1[i])
}
for _, t := range ts2 {
fmt.Println((*t).id)
}
output:
1
2