When would you use Value Vs Pointer Receivers in a real project
Fast recall
Quick revision
Core answer
Practical understanding of Value Vs Pointer Receivers.
Repeating a definition without saying when to use it.
Give one trade-off and say how you would test it.
Interview-ready guide
Interview answer
The precise contract, in two clauses:
Clause 1 — the struct itself: safe. A value receiver's u is a copy; u.Name = s, u.Age++ — every field write lands in copy storage, gone at return. The original's fields are untouched. Executed proof: Rename("Bo") through a value receiver left u.Name == "Al".
Clause 2 — data the struct POINTS to: not safe. The copy is shallow — slice/map/pointer fields share their underlying data with the original:
func (d Doc) AddTag(t string) {
d.Tags[0] = t // writes through the SHARED slice — escapes!
}
d := Doc{Tags: []string{"old"}}
d.AddTag("new")
// d.Tags[0] == "new" — the value receiver did NOT protect thisThat's the trap: the author saw a value receiver, concluded 'safe', wrote through a reference field — and the mutation escaped. The receiver kind protects exactly one layer — the field values — and reference fields' VALUES are the headers; the data beyond them is shared by construction.
The append-counter-trap: d.Tags = append(d.Tags, t) through a value receiver — the append may write into shared capacity (visible!) or allocate (private) — the caller's slice may or may not change depending on capacity. Non-deterministic-looking mutation through a value receiver is almost always this case.
So the rule for reviewers: value receivers guarantee the receiver's FIELDS are safe; any method writing through d.Slice[i], d.Map[k], or *d.Ptr is mutating shared state regardless of receiver kind — and belongs behind a pointer receiver with a comment saying so.
When would you use Value Vs Pointer Receivers in a real project
Direct Answer
It protects the STRUCT'S FIELDS — writes die in the copy. It does NOT protect data behind reference fields: slice elements, map entries, and pointees write through the shared reference and mutate the caller's data.
Deep Dive & Architecture
The shallow copy is the mechanism — the spec-level behavior: assignment (including the call-boundary copy into a value receiver) copies the struct's fields. Reference-typed fields are headers (pointer+len for slices; pointer for maps; the pointer for pointers) — the headers copy; the pointees don't. The copy and the original are two structs sharing: one backing array per slice field, one table per map field, one pointee per pointer field.
Writes through the header — d.Tags[0] = x (backing array write), d.Map[k] = v (table write), d.Ptr.Name = y (pointee write) — land in the SHARED structure: visible on both sides. Writes TO the header — d.Tags = append/new-slice, d.Ptr = other, d.Map = nil — rebind the COPY's header: private. The complete rule table for value receivers, and every surprise in this area is a row of it.
The sync.Mutex corollary — go vet's copylocks check exists because of this: a struct with an embedded mutex has a field whose header-copy doesn't even share correctly — the mutex's internal state (locked bit, waiter list) is VALUE data that must not be duplicated. Copying a locked mutex creates a second mutex claiming the same lock state — undefined behavior of the worst kind. go vet flags every copy: value receiver on a locked-fields struct, value assignment, range-copy — the linter enforces the spec's 'don't" rule that the compiler can't because it can't know the semantic.
The deep design point for interviews: Go's protection granularity is deliberately field-level, not object-level. A value receiver isn't const (no C++ const-depth), isn't a defensive copy of the object graph — it's a fast shallow copy whose semantics compose with the value/reference type distinction running through the whole language. Understanding that ONE distinction (headers share, fields don't) predicts receiver behavior, slice behavior, map behavior, and interface boxing — it's the same fact every time.
Common Mistakes & Pitfalls
Repeating a definition without saying when to use it.
Check your understanding
Answer these without looking back at the article.
- What is Value Vs Pointer Receivers, and when would you use it?
- 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.