Using a setter for a struct type does not work as anticipated

Using a setter function for a struct, but not working as anticipated:

package main

import "fmt"

type T struct { Val string }

// this setter seems not to work
func (t T) SetVal( s string ) {
        t.Val = s
}

// this setter, using ptr to T, seems to work ok
func (t *T) SetVal2( s string ) {
        (*t).Val = s
}

func main() {
        v := T{"abc"}
        fmt.Println( v )        // prints {abc}
        v.SetVal("pdq")
        fmt.Println( v )        // prints {abc}, was expecting {pdq}!
        v.SetVal2("xyz")
        fmt.Println( v )        // prints {xyz}!
}

I am missing some fundamental understanding - why doesn't SetVal work?

the behavior is similar to setting values in reflect which only works if provided a pointer to the object as versus the object itself


Here is the fundamental understanding you are missing: when a struct is passed in to a function as a pointer, the function can modify the original struct because it has a pointer to it. However, when a struct is passed in to a function via its value, then a NEW copy of the struct is actually created just for that function call, and any modifications to this new copy of the struct will not affect the original struct.

You can prove that this is the way it works by printing out the actual addresses of the structs in question:

package main

import "fmt"

type T struct { Val string }

func (t T) SetVal( s string ) {
        fmt.Printf("Address of copy is %p\n", &t);
}

func (t *T) SetVal2( s string ) {
        fmt.Printf("Pointer argument is %p\n", t);
}

func main() {
        v := T{"abc"}
        fmt.Printf("Address of v is %p\n", &v);
        v.SetVal("pdq")
        v.SetVal2("xyz")
}

The code above results in this output when I run it in the Go playground:

Address of v is 0xf840001290
Address of copy is 0xf8400013e0
Pointer argument is 0xf840001290

Notice how the first and third pointer printed out are equal, which means they are the same struct. But the second pointer is different because it is a copy.

This seems to be exactly the same way that C structs / function parameters work, by the way.


That's the difference between call by value and call by reference. If you are from C++ background , then you would know that it's the same in C++ also. And if you are from java background, then remember all object references are just pointers to the objects really..(meaning to say, when we do Node node = new Node();.. the node is holding the address of the new node object created). Therefore any method on the object(node) is actually called by reference(because node itself is a pointer).. so it reduces back to the same example as above.