In Go, methods can be declared for both value and pointer types (value/pointer receiver). This feature has been preserved since early versions of the language for explicit control over who will modify the original data. A classic problem is the need for a distinction between the semantics of value (copy, does not modify) and pointer (shared access to data and the ability to modify).
Problem — It is easy to make a mistake by declaring a method with a value receiver and not achieving the expected effect, or by calling a value method on a pointer variable.
Solution — Follow these rules:
Example code:
type Counter struct { Value int } func (c Counter) IncCopy() { c.Value++ } // value receiver func (c *Counter) IncPointer() { c.Value++ } // pointer receiver c := Counter{} c.IncCopy() // Value remains 0 c.IncPointer() // Value becomes 1
Key features:
Can you call a value receiver method on a pointer, and a pointer method on a value?
Go "under the hood" automatically dereferences pointers or takes their address, so calling is allowed if the types are compatible. But not always — with interfaces, this does not work as predictably.
var c Counter (&c).IncCopy() // Can call value method via pointer c.IncPointer() // Can call pointer method, Go will automatically take the address
What happens if a structure implements only pointer methods, but it is passed by value to an interface?
Such an object does not implement the interface if it requires pointer methods, so a panic or compilation error may occur.
type D interface { IncPointer() } func f(d D) {} c := Counter{} f(c) // error! Counter by value does not implement the interface f(&c) // correct
Will the structure change when calling a pointer receiver method if a copy of the pointer is passed?
Yes, even when the pointer is copied, the same underlying object is used — the result will be the same.
c := Counter{} p := &c p2 := p p2.IncPointer() // Value will increase
An engineer implements a structure with value receiver methods "Update". The structure is passed through an interface, but changes "disappear" — as they are working with a copy.
Pros:
Cons:
Explicit agreement in the team: all methods that change state use only pointer receivers, interfaces are implemented only with pointers, value receivers are for "extensions" and utilities.
Pros:
Cons: