liveBook · Manning

Pointer receiver

As explained in 100 Go Mistakes and How to Avoid Them

A pointer receiver is a Go method receiver declared as a pointer, so the method can observe or modify the value referenced by that pointer.

A method that must update its receiver needs a pointer receiver. With `func (c customer) add(v float64)`, Go copies the customer: adding 50 to a balance of 100 leaves the original at 100. With `func (c *customer) add(v float64)`, Go copies the pointer instead, so the original balance becomes 150. This is not pass-by-reference; the copied pointer identifies the same object. section 6.1

type customer struct {
    balance float64
}
 
func (c *customer) add(operation float64) {    #1
    c.balance += operation
}
 
func main() {
    c := customer{balance: 100.0}
    c.add(50.0)
    fmt.Printf("balance: %.2f\n", c.balance)   #2
}

The pointer-receiver version of `add` updates the original customer and prints `150.00`. section 6.1

Choose a receiver for the behavior and type, not by reflex:

  • Use a pointer when the method must change the receiver, including when a slice method must append to the receiver itself. Use one when the receiver contains a field that cannot be copied, such as a sync type. section 6.1
  • Consider a pointer for a large object to avoid copying it; benchmark when size makes the choice uncertain. A value receiver can still change an object reached through one of its pointer fields, even though the receiver itself was copied. section 6.1

A pointer receiver may be nil. A method can be called on a nil pointer if its implementation does not need to dereference it. But returning a nil `*MultiError` as an `error` does not return a nil interface: an `err != nil` check succeeds even though formatting the error prints `<nil>`. Return the pointer only when it is non-nil; otherwise return an explicit `nil`. section 6.4

A deferred call sees a later change

Given `s := &Struct{id: "foo"}`, `defer s.print()` followed by `s.id = "bar"` prints `bar` when `print` has a pointer receiver. The receiver is evaluated when `defer` runs, but the copied pointer still identifies the struct that changes. With a value receiver, the deferred call prints `foo` because it holds the earlier copy. section 6.6.2

How 100 Go Mistakes and How to Avoid Them covers it

How deep it goes
Explains copied pointers, mutation, nil receivers, and deferred receiver evaluation; defers thorough values-versus-pointers performance treatment.
Examples
Contrasts balance updates of 100 versus 150, a nil pointer returned as an error, and deferred prints of `foo` versus `bar`.
What it assumes
Assumes Go function familiarity while identifying methods as functions attached to a type and explaining value-versus-pointer receiver behavior.
What you can do after
Diagnose/debug with it when a method fails to mutate its receiver or a deferred call observes an unexpected value.
How it connects
Connects pointer receivers to value receivers, slices, sync types, nil interfaces, and defer.

Also explained in

Compare how 2 books explain pointer receiver →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of 100 Go Mistakes and How to Avoid Them. The section links point to the parts of the book it draws on.