Value Vs Pointer Receivers
Fast recall
Quick revision
Core answer
Whether you understand Value Vs Pointer Receivers beyond memorized syntax.
Giving a definition without explaining a practical trade-off.
Connect the concept to a small production example and state its complexity or failure mode.
Interview-ready guide
Interview answer
The decision in the order interviewers expect it:
- 1
Does the method mutate the receiver? Pointer — the only kind whose writes survive. (Value's writes die in the copy.)
- 2
Is the struct big? Pointer — a value receiver copies the whole struct on EVERY call. Crossover: past a handful of words, the copy exceeds the pointer's one-word indirection. Small structs (under ~64 bytes): value can be cheaper — no indirection, better register allocation, no escape forcing.
- 3
Consistency: if ANY method needs a pointer (mutation, size), make them all pointer. Mixed receivers create a split method set — the value can satisfy some interfaces, the pointer others — and that inconsistency becomes someone's interface puzzle later.
- 4
Does the value contain a mutex or other state that must not be copied? Pointer, always — copying a sync.Mutex (via value receiver or value assignment) is a bug: the copy's lock state is meaningless. go vet catches this (copylocks).
The value side's honest cases: small immutable data (time.Time — all value receivers, immutable by design); the receiver is semantically a value (math/big.Int operations take pointer because mutation; Point.Rotate SHOULD take value if it returns the rotated point); the type's underlying is already a reference — methods on maps/slices/chans/funcs take value receivers because the 'value' is a header — copying it is cheap and shares the same data.
The compile-safety tiebreaker when genuinely torn: value receivers protect the receiver from mutation BY CONSTRUCTION — if the type is meant to be used as an immutable value, value receivers enforce that in every method, and the method set says so.
func (c Counter) Get() int { return c.n } // value: read-only, small
func (c *Counter) Inc() { c.n++ } // pointer: mutates
// consistency: once one pointer method exists, make all pointerValue Vs Pointer Receivers
Direct Answer
Pointer: receiver is mutated, struct is large, type already has pointer methods, or the value contains sync/immutable-state. Value: small immutable structs, receiver is a read-only view, or the type itself is a reference (map/slice/chan).
Deep Dive & Architecture
The two costs being traded, precisely:
Value receiver's cost: a copy per call, sized by the struct (fields copied, plus any padding). For a 2-field struct: negligible. For a 50-field config: a full memcpy per method call, in the hot path — real. Compilers CAN optimize copies of receivers that don't escape and aren't modified... but they can't remove them from the ABI (the method signature says by-value) — the optimization space is within the copy's handling, not its elimination.
Pointer receiver's cost: one indirection per field access (u.Name = load(u) + offset) — nanoseconds, but nonzero, and it can inhibit some register promotion. The subtler cost: pointer receivers make the receiver's ADDRESS escape into the call — if the method stores it or the escape analyzer can't prove it doesn't, the caller's variable gets heap-allocated. A loop creating pointers to call a pointer method can become a heap-allocation-per-iteration — the classic benchmark regression.
The map/slice/chan receiver clarification interviewers reward: methods on these types use VALUE receivers not as an optimization but as CORRECTNESS — the method set is on the map/slice type; a 'pointer to a map' (*map[string]int) is nearly never what you mean (the map value itself is already a reference; a *map is a pointer to a reference — double indirection, and assigning through it can't grow the original map). func (m MyMap) Set(k, v) — the value receiver's copy shares the map's storage — mutations of the MAP contents work; only rebinding m itself would need a pointer, and rebinding a map is almost always wrong.
The consistency rule's deeper reason: a type is either a VALUE or a REFERENCE, conceptually. Mixing receivers breaks the concept — users can't predict copy semantics per method. time.Time says 'value' with every receiver; bytes.Buffer says 'reference' with every one. The receiver kind isn't per-method trivia — it's the type's identity statement, applied uniformly.
Common Mistakes & Pitfalls
Giving a definition without explaining a practical trade-off.
Check your understanding
Answer these without looking back at the article.
- When would you use Value Vs Pointer Receivers in a real project?
- What is a common mistake when using Value Vs Pointer Receivers?
- How would you compare Value Vs Pointer Receivers with an alternative?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.