Why does this method's mutation not persist, and how do you fix it?
Predict what this program prints, explain why, and fix it so the change sticks.
type BasicStruct struct{ Value string }
func (b BasicStruct) Set(s string) { b.Value = s }
func (b BasicStruct) Get() string { return b.Value }
type Doer interface {
Set(string)
Get() string
}
func doChangeAndPrint(d Doer) {
d.Set("bar")
fmt.Println(d.Get())
}
func main() {
bs := BasicStruct{Value: "foo"}
doChangeAndPrint(bs)
}
Explain the output and fix it so it prints bar.
It prints foo. A method call is sugar for passing the receiver as an argument, and Set has a value receiver, so b is a copy — b.Value = s mutates the copy and the original is untouched. Even passing &bs would still print foo, because Set copies the value regardless. Fix: make Set a pointer receiver, func (b *BasicStruct) Set(s string), and pass &bs.
- ✗Thinking a value receiver mutates the caller's struct rather than a copy
- ✗Believing passing
&bsalone fixes it without changing the receiver - ✗Assuming the code fails to compile rather than just printing the unchanged value
- →Why is a value-receiver method in the method set of both
Tand*T? - →After switching
Setto a pointer receiver, why must you pass&bsnotbs?
What it prints
The program prints foo.
A method call is syntactic sugar: bs.Set("bar") is Set(bs, "bar"). The receiver b BasicStruct is a value, so the method gets a copy of the struct. The assignment b.Value = s mutates the copy, and bs in main stays {Value: "foo"}.
The subtlety: even calling doChangeAndPrint(&bs) still prints foo. Set is declared with a value receiver, so calling it through a pointer copies the value into b anyway.
The fix
Make Set a pointer-receiver method and pass a pointer:
func (b *BasicStruct) Set(s string) { b.Value = s }
func main() {
bs := &BasicStruct{Value: "foo"}
doChangeAndPrint(bs) // prints bar
}
Now *BasicStruct satisfies Doer, and Set mutates the very struct bs points at. Value-receiver methods are in the method set of both T and *T; pointer-receiver methods only of *T.