How do you decide between value and pointer receivers?
Question 83MediumGo 1.22 to 1.25
Use a pointer receiver when any of these apply:
- The method mutates the receiver.
- The struct is large, so copying it on every call is expensive.
- The struct contains fields that must not be copied (
sync.Mutex,sync.WaitGroup,atomic.Int64,bytes.Buffer, or anything with anoCopyfield). - Other methods of the type already use pointer receivers. Keep them consistent.
Use a value receiver for small, immutable value types (time.Time, a Point), for maps, funcs and chans (already reference-like), and for slices when the method does not reslice or append.
type Account struct {
mu sync.Mutex
balance int64
}
// BUG: value receiver copies the mutex; each call locks its own copy
func (a Account) Balance() int64 {
a.mu.Lock()
defer a.mu.Unlock()
return a.balance
}
// go vet: "Balance passes lock by value: Account contains sync.Mutex"
Mixing receiver kinds is legal, but it makes the method set of T a strict subset of the method set of *T, which confuses readers. A value receiver also gives weaker thread-safety than it appears to: the copy is taken when the method is called, and that copy can race with concurrent writers.
More on Interfaces, Methods & Embedding
- Q81Explain method sets. Why does
*Tsatisfy an interface whenTdoes not? - Q82Why doesn't
m["k"].Inc()compile whenInchas a pointer receiver? What other values are not addressable? - Q84Compare
v := x.(T)withv, ok := x.(T). What happens whenTis an interface type? - Q85Explain type switch semantics. What does this print?
- Q86Is struct embedding inheritance? How does method promotion work?
- Q87What does this print? (no virtual dispatch with embedding)