Skip to content

Day 015 — Methods & Receivers (Value vs Pointer)

Month 1 · Week 3 · ⬅ Day 014 · Day 016 ➡ · Journal index

🎯 Learning Objective

Attach methods to types and choose correctly between value and pointer receivers, understanding how that choice affects mutation, copying, and method sets.

📚 Topics

  • Method declaration syntax: the receiver between func and the name
  • Value vs pointer receivers · when each mutates
  • Method sets · addressability · automatic &/* by the compiler
  • Methods on any named type (not just structs)

📖 Reading / Sources

📝 Notes

  • A method is a function with a receiver argument: func (c Counter) Value() int. The receiver names the type the method is bound to.
  • Value receiver operates on a copy — mutations do not persist. Pointer receiver operates on the original — mutations persist. This is the single most important distinction → [[value-vs-pointer-receivers]].
  • Use a pointer receiver when: (a) the method mutates the receiver, (b) the struct is large and copying is wasteful, or © consistency — if any method needs a pointer, give them all pointers so the method set is uniform.
  • Use a value receiver for small, immutable values (e.g. a time.Time-like struct, coordinates). Maps, slices, channels, and funcs are reference-like, so a value receiver still "sees" shared backing data — but appending to a slice field still needs a pointer to persist a new header → [[slice-aliasing]].
  • Method sets: the method set of T is methods with value receivers; the method set of *T is methods with both value and pointer receivers. This matters for [[interfaces]] — a value of type T does not satisfy an interface whose implementation uses a pointer receiver.
  • The compiler auto-takes the address (&x) or dereferences (*p) when calling, but only if the value is addressable. A map element or the return value of a function is not addressable, so m[k].PointerMethod() won't compile.
  • You can define methods on any named type you own, including type Celsius float64 — not just structs. You cannot define methods on types from other packages.

💻 Code Examples

type Counter struct{ n int }

func (c Counter) Value() int { return c.n } // value receiver: read-only copy
func (c *Counter) Inc()      { c.n++ }       // pointer receiver: mutates original

func main() {
    c := Counter{}
    c.Inc() // compiler rewrites to (&c).Inc() because c is addressable
    c.Inc()
    fmt.Println(c.Value()) // 2
}

Full code: examples/month-01/methods/main.go · Run: go run ./examples/month-01/methods

🏋️ Exercises / Practice

Exercise Status Link
Counter with pointer-receiver Inc/Reset and value-receiver Value exercises/month-01/week-3/counter
Stack push/pop showing why mutation needs * exercises/month-01/week-3/counter

🐛 Mistakes Made

  • Gave Inc a value receiver and wondered why c.n never changed — it mutated a copy.
  • Tried calling a pointer method on a map element (scores["a"].Inc()) → "cannot call pointer method on map index expression" (not addressable).

❓ Open Questions

  • Is there ever a correctness reason to mix value and pointer receivers on one type? (Convention says no — keep them consistent.)

🧠 Active Recall (answer without looking)

  1. Q: Why does a value-receiver method fail to mutate the receiver?

    A The receiver is passed by value, so the method works on a *copy*; changes are discarded when the method returns. Use a pointer receiver to mutate the original.

  2. Q: Does a value of type T satisfy an interface implemented with a pointer receiver?

    A No. The method set of `T` includes only value-receiver methods; pointer-receiver methods belong to `*T`. You'd need `&v` (and it must be addressable).

🪶 Feynman Reflection

A method is just a function with a hidden first argument: the receiver. If you pass that argument by value, the function edits a photocopy and your original stays untouched. If you pass it by pointer, the function edits the real thing. Everything about "value vs pointer receiver" follows from that one fact.

🕳️ Knowledge Gaps

  • Exactly when the compiler refuses the automatic & (addressability rules) — needs more reps.

✅ Summary

I can attach methods to my own types and choose value vs pointer receivers deliberately, knowing the choice controls mutation, copying cost, and which method set satisfies an interface.

⏭️ Next Steps / Prep for Tomorrow

  • Day 016: interfaces and implicit satisfaction — and how method sets decide who implements what.

Time spent Difficulty Confidence
90 min 🟦🟦⬜⬜⬜ 🟦🟦🟦⬜⬜

Suggested commit: feat(examples): methods and value vs pointer receivers (day 015)