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.
func (v T) Method() // gets a copy func (p *T) Method() // gets the originalA 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
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.
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.
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.
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