I don't understand the behavior of the following piece of code. In creating a list of matching structs as a slice of struct pointers, the code always prints the last element of original array (which actually wasn't a match)—it prints 12 and 12. However, if I change matches to be []Widget instead of []*Widget, then it will print 10 and 11.
Why is this?
package main
import (
"fmt"
)
func main() {
type Widget struct {
id int
attrs []string
}
widgets := []Widget{
Widget{
id: 10,
attrs: []string{"blah", "foo"},
},
Widget{
id: 11,
attrs: []string{"foo", "bar"},
},
Widget{
id: 12,
attrs: []string{"xyz"},
},
}
var matches []*Widget
for _, w := range widgets {
for _, a := range w.attrs {
if a == "foo" {
matches = append(matches, &w)
break
}
}
}
for _, m := range matches {
fmt.Println(m.id)
}
}
That's because when you use the pointers you are adding &w
to the array.
Note that w
is actually the local variable used in the loop, so that's not the address you want to add to the matches
array.
(even though the value of the variable w
changes through the loop, its address stays the same)
When the loop ends, w
ends up with the last value so that's why it prints 12
two times.
You need to add the address of the element that matched instead.
If you do this:
matches = append(matches, &widgets[i])
Then it'd work fine with pointers as well.
Modified Go playground for you to test it:
https://play.golang.org/p/YE-cokyEHu
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With