Go by Example: Receiver Methods
Methods in Go can have either value or pointer receivers. This example explains the difference, when to use each, and how they affect the mutability of the receiver.
Code
package main
import (
"fmt"
"math"
)
type Vertex struct {
X, Y float64
}
// 1. Value Receiver
// Operates on a copy of the struct.
// Cannot modify the original struct.
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
// 2. Pointer Receiver
// Operates on the actual struct (via pointer).
// Can modify the original struct.
func (v *Vertex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
func main() {
v := Vertex{3, 4}
// Call value receiver
// 'v' is copied passed to Abs
fmt.Println("Abs:", v.Abs())
// Call pointer receiver
// Go automatically takes the address (&v)
v.Scale(10)
fmt.Println("Scaled:", v)
// Pointers can call value receivers too
p := &Vertex{3, 4}
fmt.Println("Ptr Abs:", p.Abs()) // Go automatically dereferences (*p)
}
Explanation
In Go, methods are functions that are attached to a specific type, known as the receiver, which can be defined either as a value or a pointer. The choice between a value receiver (e.g., func (t T)) and a pointer receiver (e.g., func (t *T)) is semantically significant: a value receiver operates on a complete copy of the data, ensuring immutability of the original instance but potentially incurring performance overhead for large structures, whereas a pointer receiver operates on the memory address of the instance, allowing the method to mutate the original state and avoiding the cost of copying. This distinction is fundamental to Go's design, enabling developers to explicitly control memory access patterns and side effects.
The Go compiler provides syntactic sugar that often abstracts the mechanics of calling these methods, automatically referencing or dereferencing values to match the receiver type when the variable is addressable. However, understanding the underlying behavior is crucial for correctness, particularly when implementing interfaces or working with concurrency; for instance, methods that modify state must use pointer receivers to ensure changes persist, while methods on types intended to be immutable or concurrent-safe often benefit from value receivers. Additionally, consistency is a key idiomatic principle—if a type has any method with a pointer receiver, convention dictates that all exported methods should typically use pointer receivers to maintain a uniform method set.
- Value Receiver: Receives a copy. Safe, immutable, but copies data.
- Pointer Receiver: Receives a reference. Can mutate data, efficient for large structs.
- Automatic Dispatch:
v.Method()works whethervis a value or pointer (if addressable). - Consistency: Don't mix receiver types for a struct if possible.

