I read The Go Programming Language Book recently, the good resource for learning golang programming language. There is a paragraph in 6.2 section about copy instance of type
When calling a method, the value the method is called on is first copied, and that copy is passed / used as the receiver.
If a type only has methods with value receivers, that means no matter what the methods do inside, and no matter what methods you (or anyone else) call, the methods won't be able to change the original value, because –as noted above– only a copy is passed and the method could only modify the copy – and not he original.
So this means if you copy the value, you don't have to worry, neither the methods called on the original nor on the copy can't / won't modify the value(s).
Not, when the type has methods with pointer receivers. If a method has a pointer receiver, the method can change / modify the pointed value, which is not a copy, it's the original value (only the pointer is a copy but it points to the original value).
Let's see an example. We create an int
wrapper type, which has 2 fields: an int
and an *int
. We intend to store the same number in both fields, but one is a pointer (and we store the int
in the pointed value):
type Wrapper struct {
v int
p *int
}
To make sure both values (v
and *p
) are the same, we provide a Set()
method, which sets both:
func (w *Wrapper) Set(v int) {
w.v = v
*w.p = v
}
Wrapper.Set()
has a pointer receiver (*Wrapper
) as it has to modify the value (which is of type Wrapper
). No matter what number we pass to Set()
, we can be sure that once Set()
returns, both v
and *p
will be the same, and equal to the number passed to Set()
.
Now if we have a value of Wrapper
:
a := Wrapper{v: 0, p: new(int)}
We can call the Set()
method on it:
a.Set(1)
The compiler will automatically take the address of a
to use as the receiver, so the above code means (&a).Set(1)
.
We'd expect that any value of type Wrapper
has the same number stored in Wrapper.v
and *Wrapper.pv
, if only the Set()
method is used to change the fields' values.
Now let's see the problem if we make a copy of a
:
a := Wrapper{v: 0, p: new(int)}
b := a
fmt.Printf("a.v=%d, a.p=%d; b.v=%d, b.p=%d\n", a.v, *a.p, b.v, *b.p)
a.Set(1)
fmt.Printf("a.v=%d, a.p=%d; b.v=%d, b.p=%d\n", a.v, *a.p, b.v, *b.p)
Output (try it on the Go Playground):
a.v=0, a.p=0; b.v=0, b.p=0
a.v=1, a.p=1; b.v=0, b.p=1
We made a copy of a
(stored it in b
), and printed the values. So far so good. Then we called a.Set(1)
, after which a
is still good, but internal state of b
became invalid: b.v
does not equal to *b.p
anymore. The explanation is quite clear: when we made a copy of a
(which is a struct
type), that copies the values of its fields (including the pointer p
), and the pointer in b
will point to the same value as the pointer in a
. Hence modifying the pointed value will affect both copies of Wrapper
, but we have 2 distinct v
fields (they are non-pointers).
If you have methods with pointer receivers, you should work with pointer values.
Note that if you would copy a value of *Wrapper
, everything would still be cool:
a := &Wrapper{v: 0, p: new(int)}
b := a
fmt.Printf("a.v=%d, a.p=%d; b.v=%d, b.p=%d\n", a.v, *a.p, b.v, *b.p)
a.Set(1)
fmt.Printf("a.v=%d, a.p=%d; b.v=%d, b.p=%d\n", a.v, *a.p, b.v, *b.p)
Output (try it on the Go Playground):
a.v=0, a.p=0; b.v=0, b.p=0
a.v=1, a.p=1; b.v=1, b.p=1
In this case a
is a pointer, it's of type *Wrapper
. We made a copy of it (stored it in b
), called a.Set()
, and both the internal state of a
and b
remained valid. Here we only have one Wrapper
value, a
only holds a pointer to it (its address). When we copy a
, we only make a copy of the pointer value, and not the struct
value (of type Wrapper
).