Pointer vs Value Receivers

A pointer receiver can change the value it was called on; a value receiver works on a copy. Pick one style per type.

Syntaxfunc (v T) Method() // gets a copy func (p *T) Method() // gets the original

A receiver can be written two ways. func (a Account) ... is a value receiver: the method gets a copy of the Account. func (a *Account) ... is a pointer receiver: the method gets the address, so it works on the original.

When each one is right

Use a pointer receiver if the method changes the value. Changes made through a value receiver are made to the copy and thrown away when the method returns. Use a pointer receiver also when the type is large, because a value receiver copies every field on every call. Use a value receiver for small types that the method only reads, where copying a few words is cheaper than following an address.

The shorthand you are already using

Go inserts & and * for you when it can. If acc is a variable, acc.Deposit(50) is shorthand for the longhand (&acc).Deposit(50). If p is a pointer, p.Balance is shorthand for (*p).Balance and p.Total() for (*p).Total(). Both forms compile and mean the same thing.

That shorthand needs something with an address. A variable, a slice element and a struct field all have one. A map element and the result of a function call do not, so m["a"].Deposit(1) will not compile when Deposit has a pointer receiver. Store pointers in the map instead.

Keep one type consistent

If any method on a type needs a pointer receiver, give every method on that type a pointer receiver. Mixed receivers make a type harder to read, and the set of methods available differs between T and *T: only *T has all of them. That difference matters later, when a type has to satisfy an interface.

Example

Example · go
package main

import "fmt"

type Account struct {
	Balance int
}

// Deposit has a pointer receiver, so it changes the Account it was called on.
func (a *Account) Deposit(n int) {
	a.Balance += n
}

// WithBonus has a value receiver, so it changes a copy and returns it.
func (a Account) WithBonus(n int) Account {
	a.Balance += n
	return a
}

func main() {
	acc := Account{Balance: 100}

	acc.Deposit(50) // shorthand for (&acc).Deposit(50)
	fmt.Println(acc.Balance)

	bigger := acc.WithBonus(1000)
	fmt.Println(acc.Balance, bigger.Balance)

	p := &acc
	fmt.Println(p.Balance) // shorthand for (*p).Balance
	p.Deposit(1)
	fmt.Println(acc.Balance)
}

// Output:
// 150
// 150 1150
// 150
// 151

When to use it

  • Write a Deposit method that really changes the Account's balance.
  • Stop copying a 1KB struct on every method call by switching to a pointer receiver.
  • Decide the receiver style for a new type and keep every method on it the same.
  • Track down a method that appears to do nothing because it mutated a copy.

More examples

A value receiver reached through a pointer still copies

Having a pointer in hand does not make a value-receiver method mutate anything. This is worth reading twice, because the call looks identical to the working one and the compiler reports nothing. If a method is meant to change the receiver, its receiver must be a pointer.

Example · go
package main

import "fmt"

type Point struct {
	X, Y int
}

// Move has a value receiver: the copy moves, the original does not.
func (p Point) Move(dx, dy int) {
	p.X += dx
	p.Y += dy
}

// MoveInPlace has a pointer receiver: the original moves.
func (p *Point) MoveInPlace(dx, dy int) {
	p.X += dx
	p.Y += dy
}

func main() {
	p := &Point{X: 1, Y: 1}

	// p.Move means (*p).Move, so a copy is made and then discarded.
	p.Move(10, 10)
	fmt.Println(*p)

	p.MoveInPlace(10, 10)
	fmt.Println(*p)
}

// Output:
// {1 1}
// {11 11}

Map elements have no address

With map[string]Tally instead, m["a"].Inc() is a compile error, because Go cannot take the address of a map element. The two usual fixes are storing pointers, as here, or reading the element into a variable, calling the method on that, and assigning it back.

Example · go
package main

import "fmt"

type Tally struct {
	N int
}

func (t *Tally) Inc() {
	t.N++
}

func main() {
	// A map of pointers works: the element already is an address.
	// In this literal, {} is shorthand for &Tally{}.
	m := map[string]*Tally{"a": {}}
	m["a"].Inc()
	fmt.Println(m["a"].N)

	// A slice element has an address, so the compiler can take it for you.
	s := []Tally{{}}
	s[0].Inc() // shorthand for (&s[0]).Inc()
	fmt.Println(s[0].N)
}

// Output:
// 1
// 1

One type, one receiver style

Contents could have used a value receiver, but then one type would have two styles and readers would have to check each method before calling it. The house rule is simpler: once any method needs a pointer, they all take one.

Example · go
package main

import "fmt"

// Buffer holds a 1KB array, so copying it on every call would be wasteful.
type Buffer struct {
	data [1024]byte
	used int
}

// Write must mutate, so it takes a pointer receiver.
func (b *Buffer) Write(p []byte) {
	copy(b.data[b.used:], p)
	b.used += len(p)
}

// Contents only reads, but it takes a pointer too, for consistency.
func (b *Buffer) Contents() string {
	return string(b.data[:b.used])
}

func main() {
	var b Buffer // the zero value is already usable
	b.Write([]byte("hello "))
	b.Write([]byte("world"))
	fmt.Println(b.Contents(), b.used)
}

// Output:
// hello world 11

Discussion

  • Be the first to comment on this lesson.