Abhishek kapoor
Abhishek kapoor

Reputation: 151

Pointers confusion in Go

I am having a hard time understanding the below code, I have it working but still don't understand it. It would really helpful if someone can demystify it

In below code Flags name is not updated to "Changed"

package main

import "fmt"

type Command struct {
   Name  string
   Flags []Flag
}

type Flag struct {
   Name      string
   Shorthand string
}

func getCommand() *Command {
   return &Command{Name: "Hello", Flags: []Flag{{"version", "v"}}}
}

func change(cmd *Command) {
   for _, flg := range cmd.Flags {
       flg.Name = "Changed"
   }

}

func main() {
   cmd := getCommand()
   change(cmd)
   fmt.Println(cmd.Flags[0])

}

Correct code. Flags Name is changed to "Changed"

package main

import "fmt"

type Command struct {
    Name  string
    Flags []*Flag
}

type Flag struct {
    Name      string
    Shorthand string
}

func getCommand() *Command {
    return &Command{Name: "Hello", Flags: []*Flag{{"version", "v"}}}
}

func change(cmd *Command) {
    for _, flg := range cmd.Flags {
        flg.Name = "Changed"
    }

}

func main() {
    cmd := getCommand()
    change(cmd)
    fmt.Println(cmd.Flags[0])

}

I am sure, my confusion might be trivial, but it has wasted my couple of hours

Upvotes: 0

Views: 90

Answers (2)

Abir Ganguly
Abir Ganguly

Reputation: 13

for _, flg := range cmd.Flags {
        flg.Name = "Changed"
    }

This part is the exact culprit. The first value returned by range is the index(which is ignored here), the second value is a copy of the actual data. When you are using it inside the loop(flg.Name=...), You are assigning the value to Name filed of the copy. You may do this:

for index, _ := range cmd.Flags {
        cmd.Flags[index].Name = "Changed"
    }

In the second case, you are assigning to Name field of a copy of reference(or memory address) to the Flag, so the value at that memory address get changed.

Upvotes: 1

Darshan Rivka Whittle
Darshan Rivka Whittle

Reputation: 34031

In your first version, when you do this:

for _, flg := range cmd.Flags {
    flg.Name = "Changed"
}

flg is a local variable of type Flag. On each iteration of the for loop, range sets flg to a copy of the next item in cmd.Flags. So when you change that, nothing changes outside that loop.

In your second version, when you do that loop, flg is a local variable of type *Flag, so when range sets it to a copy of the next item in cmd.Flags, flg is pointing to the data you are trying to mutate, and mutating it actually changes that data.

Upvotes: 1

Related Questions