I am using a for range
loop in Go to iterate through a slice of structs.
In each loop, I a pointer to the current item to a variable.
I am confused why the pointer changes value in the next loop.
For example this code:
package main
import "fmt"
type t struct {
val int
}
func main() {
l := []t{{1}, {2}}
var p *t
for _, i := range l {
fmt.Println("begin", p)
p = &i
fmt.Println("end", p)
}
}
I would expect to produce:
begin <nil>
end &{1}
begin &{1}
end &{2}
But actually does:
begin <nil>
end &{1}
begin &{2}
end &{2}
For reference, in my actual code, I am checking for a condition during the loop, and returning the current item and previous one. So I am trying to save a pointer to it, so that in the next iteration it will have access to the previous as well.
The problem is that you're taking the address of the loop/range variable and not the address of the item in slice. However, you're just making a lot of unnecessary work for yourself. For one, why don't you use the i, v := range
or better yet i, _ :=
and then you can do i-1
to get the previous item? Secondly, even if you want it saved in a pointer, still use this syntax and then assign p = &l[i]
so you have the address of the item in the slice rather than the address of the loop/range variable.
People are way too eager to use for/each style constructs when it's obviously better to work with the index... If you want index-1 on every iteration, using the index should be your go to way of doing that.
Building off Tim's comment, it seems like you can copy the value on each loop, instead of the pointer, and dereference it after.
package main
import "fmt"
type t struct {
val int
}
func main() {
l := []t{{1}, {2}}
var p t
var i t
for _, i = range l {
fmt.Println("begin", &p)
p = i
fmt.Println("end", &p)
}
}
Another option is to get the pointer to the current item by using the index:
package main
import "fmt"
type t struct {
val int
}
func main() {
l := []t{{1}, {2}}
var p *t
for index, _ := range l {
fmt.Println("begin", p)
p = &l[index]
fmt.Println("end", p)
}
}