liveBook · Manning

Value receiver

As explained in 100 Go Mistakes and How to Avoid Them

A value receiver is a Go method receiver declared as a value, so the method operates on a copy of the receiver value.

A value receiver makes a method operate on a copy of its receiver. If a method changes a field on that copy, the original value does not change. This is easy to miss when a method name suggests mutation. section 6.1

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

Calling `add(50)` leaves the original balance at `100.00`: `c.balance += v` changes only the copied receiver. section 6.1

Use a pointer receiver when the method must change the original value. In the pointer-receiver version of `add`, the same call changes the balance to `150.00`. Go still copies the receiver argument, but that argument is a pointer rather than the whole struct. section 6.1

A copied struct can still reach shared data

Suppose a struct holds `data *data`, and `add` has a value receiver. The struct is copied, but its pointer still identifies the same data. Changing `c.data.balance` therefore changes the balance visible through the original struct. Do not treat a value receiver as a guarantee that a method cannot change anything shared. section 6.1

Receiver choice matters for deferred methods, too. A deferred call evaluates its receiver immediately. With a value receiver, changing the original struct before the deferred method runs does not change the saved copy; with a pointer receiver, a later change to the pointed-to struct is visible. section 6.6.2

func main() {
    s := Struct{id: "foo"}
    defer s.print()           #1
    s.id = "bar"              #2
}
 
type Struct struct {
    id string
}
 
func (s Struct) print() {
    fmt.Println(s.id)         #3
}

The deferred value-receiver method prints `foo`, not `bar`, because its receiver was copied before `s.id` changed. section 6.6.2

A more serious copying mistake is a value-receiver method on a struct containing `sync.Mutex`. Each call copies the mutex, so callers do not lock the same critical section; concurrent increments can race. Make the receiver a pointer, or store a pointer to the mutex and initialize it. `go vet` can flag a method that passes a lock by value. section 9.14

type Counter struct {
    mu       sync.Mutex
    counters map[string]int
}
 
func NewCounter() Counter {                    #1
    return Counter{counters: map[string]int{}}
}
 
func (c Counter) Increment(name string) {
    c.mu.Lock()                                #2
    defer c.mu.Unlock()
    c.counters[name]++
}

This `Increment` method copies `Counter`—including its mutex—on every call, despite locking before updating the map. section 9.14

Choose by behavior and type, not by a blanket performance rule. Use a pointer receiver when mutation or a non-copyable field requires one; consider one for a large receiver to avoid an extensive copy. Benchmark when size makes the performance choice uncertain. section 6.1 section 6.7

How 100 Go Mistakes and How to Avoid Them covers it

How deep it goes
Traces observable effects of receiver copies through mutation, defer, and mutex use, while leaving detailed pointer-versus-value performance analysis aside.
Examples
Contrasts balance updates, deferred printing of `foo` versus `bar`, and a concurrent counter whose copied mutex fails to protect updates.
What it assumes
Assumes familiarity with Go methods and structs; refreshes how value and pointer receivers are passed.
What you can do after
Diagnose/debug with it when a method fails to mutate its original receiver or copying a mutex causes a race.
How it connects
Connects pointer receivers, defer, pointer fields, sync.Mutex, data races, and go vet to receiver-copy behavior.

Also explained in

Compare how 3 books explain value 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.