Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Slice of structs vs a slice of pointers to structs

Tags:

go

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)
    }

}
like image 318
eblood Avatar asked Aug 02 '17 18:08

eblood


1 Answers

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

like image 121
eugenioy Avatar answered Sep 28 '22 15:09

eugenioy