-2

I am new to Golang. I have this simple code, that I can't get to work; the problem is that after call the method LoadGroups, the "main" function doesn't see the changes:

package main

import "fmt"

type Group struct {
    Name string
}

type Configuration struct {
    Groups []Group
}

func NewConfiguration() (error, *Configuration) {
    conf := Configuration{}
    conf.LoadGroups()
    fmt.Print("Final number of groups: ", len(conf.Groups))
    return nil, &conf
}

func (conf Configuration) LoadGroups() {
    for i := 0; i < 5; i++ {
        conf.Groups = append(conf.Groups, Group{Name: "Group " + string(i)})
        fmt.Println("Current number of groups: ", len(conf.Groups))
    }
}

func main() {
    NewConfiguration()
}

Playground: https://play.golang.org/p/VyneKpjdA-

okelet
  • 627
  • 1
  • 7
  • 22
  • 3
    Please try the "Tour of Go", for example: https://tour.golang.org/methods/4 – JimB Oct 17 '17 at 19:39
  • 2
    Also, note that the same problem will arise with conf.Groups: if you want to mutate them, it should be `[]*Group` instead of `[]Group` (Playground to demonstrate the problem: https://play.golang.org/p/mTniuVKJwy ) – T. Claverie Oct 17 '17 at 19:54
  • From now on I think I will always use pointers... – okelet Oct 17 '17 at 19:58
  • 1
    Always using pointers isn't a good solution. You should use pointers when pointers are appropriate. Always using pointers increases pressure on the garbage collector. – Adrian Oct 17 '17 at 20:04
  • So, passing a copy of the object instead of by reference is better? Shouldn't a copy of the object take more memory space than the reference? Or is it better anyway than make the garbage collector work? – okelet Oct 17 '17 at 20:16
  • @okelet: function argument values are on the stack, so size isn't really a concern. Pointers aren't a _horrible_ default, but you don't always need them. Maybe read https://stackoverflow.com/questions/23542989/pointers-vs-values-in-parameters-and-return-values – JimB Oct 17 '17 at 20:35
  • Great post, thanks all for your help – okelet Oct 17 '17 at 21:28

2 Answers2

4

You are modifying a copy of the Configuration, not the Configuration itself.

The method LoadGroups should take a pointer to a Configuration instead:

package main

import "fmt"

type Group struct {
    Name string
}

type Configuration struct {
    Groups []Group
}

func NewConfiguration() (error, *Configuration) {
    conf := &Configuration{}
    conf.LoadGroups()
    fmt.Print("Final number of groups: ", len(conf.Groups))
    return nil, conf
}

func (conf *Configuration) LoadGroups() {
    for i := 0; i < 5; i++ {
        conf.Groups = append(conf.Groups, Group{Name: "Group " + string(i)})
        fmt.Println("Current number of groups: ", len(conf.Groups))
    }
}

func main() {
    NewConfiguration()
}
T. Claverie
  • 9,953
  • 1
  • 12
  • 26
  • Thanks, things are different from Java. So, to avoid problems (and work like Java), all struct functions should be pointers and the caller object? – okelet Oct 17 '17 at 19:43
  • 1
    Yup @okelet. All (almost) methods should take pointers to structs. There is no syntax difference when using a plain struct or a pointer, so you won't feel it. – T. Claverie Oct 17 '17 at 19:47
1

The LoadGroups method must be a receiver of *Configuration, instead of Configuration. Right now the method LoadGroup is being executed on a fresh copy of Configuration each time is called, therefore its changes are not reflected in the original caller. By making it of type *Configuration, all calls to the method LoadGroups sharing the same pointer change the same Configuration instance.

lamg
  • 364
  • 2
  • 6