Skip to content
Lesson 6 of 9

Step 1 of 6 · Reading · ~4 min

Learn

Pointers and Methods

Pointer Receivers vs Value Receivers

By the end you can choose between a copy and the caller's variable for a method, change structs held in a map, and predict which types satisfy an interface. In Go Fundamentals you passed *int so a function could change the caller's variable, and wrote methods that received a copy; a pointer receiver joins the two.

A receiver is a parameter

go

m.Add(5) works because m is a variable, so Go rewrites it to (&m).Add(5).

Pointers to structs

go

A constructor is a plain function, conventionally NewT, that returns a ready pointer: func NewMeter(start int) *Meter { return &Meter{units: start} }.

Choosing a receiver

Use *T when the method changes the receiver, the struct is large, or copies would be wrong; a value receiver suits small immutable things. Pick one kind per type.

The method-set rule

The method set of T holds only the value-receiver methods. The method set of *T holds both kinds. An interface is satisfied by a type whose method set has all its methods:

go

An interface's value has no address to hand to such a method. var _ Resetter = (*Meter)(nil) stops compiling if *Meter ever stops satisfying Resetter.

Where the pointer method is refused

go

Map elements have no address, because a map may move them as it grows. Either copy out, change and write back (m := bins[k]; m.Add(1); bins[k] = m), or store pointers:

go

Other receivers

A named non-struct type can have methods. One that grows a slice needs the pointer, because append may return a new slice to assign back through *s:

go

A method value is a method bound to a receiver. A pointer receiver binds the address, so later changes show:

go

The typed-nil trap from Interfaces returns with methods: a nil *Fault stored in an error still carries a type, so the interface is non-nil:

go

Return a literal nil on success instead.

Common mistakes

  • A value receiver on a method that changes state. It compiles, and the total stays put, as AddCopy left the reading at 0.
  • Keeping structs by value in a map. m[k].field = v does not compile, and copy-out without the write-back silently loses the change.
  • Using a lookup result without a nil check. An unknown key yields nil, and the first field access panics.
  • Returning a nil *T as an interface. The caller's err != nil is true, and the first method call that touches a field panics.

Your exercise

Build a command-driven ledger whose accounts live in a map and change through pointer-receiver methods. The starter reads and splits each line. The likeliest mistake is a lookup that changes a copy, or one that forgets an unknown name returns nil.

Up nextEmbedding and CompositionPointers and Methods

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…