HiveBrain v1.2.0
Get Started
← Back to all entries
principlegoCritical

Value receiver vs. pointer receiver

Submitted by: @import:stackoverflow-api··
0
Viewed 0 times
receiverpointervalue

Problem

It is very unclear for me in which case I would want to use a value receiver instead of always using a pointer receiver.

To recap from the docs:

type T struct {
    a int
}
func (tv  T) Mv(a int) int         { return 0 }  // value receiver
func (tp *T) Mp(f float32) float32 { return 1 }  // pointer receiver


The docs also say "For types such as basic types, slices, and small structs, a value receiver is very cheap so unless the semantics of the method requires a pointer, a value receiver is efficient and clear."

First point they docs say a value receiver is "very cheap", but the question is whether it is cheaper than a pointer receiver. So I made a small benchmark (code on gist) which showed me, that pointer receiver is faster even for a struct that has only one string field. These are the results:

// Struct one empty string property
BenchmarkChangePointerReceiver  2000000000               0.36 ns/op
BenchmarkChangeItValueReceiver  500000000                3.62 ns/op

// Struct one zero int property
BenchmarkChangePointerReceiver  2000000000               0.36 ns/op
BenchmarkChangeItValueReceiver  2000000000               0.36 ns/op


(Edit: Please note that second point became invalid in newer go versions, see comments.)

Second point the docs say that a value receiver it is "efficient and clear" which is more a matter of taste, isn't it? Personally I prefer consistency by using the same thing everywhere. Efficiency in what sense? Performance wise it seems pointer are almost always more efficient. Few test-runs with one int property showed minimal advantage of Value receiver (range of 0.01-0.1 ns/op)

Can someone tell me a case where a value receiver clearly makes more sense than a pointer receiver? Or am I doing something wrong in the benchmark? Did I overlook other factors?

Solution

Note that the FAQ does mention consistency

Next is consistency. If some of the methods of the type must have pointer receivers, the rest should too, so the method set is consistent regardless of how the type is used. See the section on method sets for details.

As mentioned in this thread:

The rule about pointers vs. values for receivers is that value methods can
be invoked on pointers and values, but pointer methods can only be invoked
on pointers

Which is not true, as commented by Sart Simha

Both value receiver and pointer receiver methods can be invoked on a correctly-typed pointer or non-pointer.

Regardless of what the method is called on, within the method body the identifier of the receiver refers to a by-copy value when a value receiver is used, and a pointer when a pointer receiver is used: example.

Now:

Can someone tell me a case where a value receiver clearly makes more sense then a pointer receiver?

The Code Review comment can help:

  • If the receiver is a map, func or chan, don't use a pointer to it.



  • If the receiver is a slice and the method doesn't reslice or reallocate the slice, don't use a pointer to it.



  • If the method needs to mutate the receiver, the receiver must be a pointer.



  • If the receiver is a struct that contains a sync.Mutex or similar synchronizing field, the receiver must be a pointer to avoid copying.



  • If the receiver is a large struct or array, a pointer receiver is more efficient. How large is large? Assume it's equivalent to passing all its elements as arguments to the method. If that feels too large, it's also too large for the receiver.



  • Can function or methods, either concurrently or when called from this method, be mutating the receiver? A value type creates a copy of the receiver when the method is invoked, so outside updates will not be applied to this receiver. If changes must be visible in the original receiver, the receiver must be a pointer.



  • If the receiver is a struct, array or slice and any of its elements is a pointer to something that might be mutating, prefer a pointer receiver, as it will make the intention more clear to the reader.



  • If the receiver is a small array or struct that is naturally a value type (for instance, something like the time.Time type), with no mutable fields and no pointers, or is just a simple basic type such as int or string, a value receiver makes sense.



A value receiver can reduce the amount of garbage that can be generated; if a value is passed to a value method, an on-stack copy can be used instead of allocating on the heap. (The compiler tries to be smart about avoiding this allocation, but it can't always succeed.) Don't choose a value receiver type for this reason without profiling first.

  • Finally, when in doubt, use a pointer receiver.



(although, see John's comment in the last section)

Note on "If the receiver is a slice and the method doesn't reslice or reallocate the slice, don't use a pointer to it."

The statement is suggesting that if you have a method that reslices or reallocates the slice, then you should use a pointer receiver.

In other words, if you modify the slice within the method, such as appending elements or changing the length/capacity of the slice, it's recommended to use a pointer receiver.

In the case of implementing deletion and insertion methods for a slice type, you will likely be modifying the slice (changing its length, appending or removing elements). Therefore, you should use a pointer receiver for these methods.

Example (playground):
package main

import "fmt"

type MySlice []int

func (s *MySlice) Insert(index int, value int) {
// Insert value at index and shift elements
s = append((s)[:index], append([]int{value}, (*s)[index:]...)...)
}

func (s *MySlice) Delete(index int) {
// Remove the element at index and shift elements
s = append((s)[:index], (*s)[index+1:]...)
}

func main() {
s := MySlice{1, 2, 3, 4, 5}
s.Insert(2, 42)
fmt.Println(s) // Output: [1 2 42 3 4 5]

s.Delete(2)
fmt.Println(s) // Output: [1 2 3 4 5]
}


In this example, the Insert and Delete methods are modifying the slice by appending and removing elements.

As a result, a pointer receiver is used to ensure the modifications are visible outside the method.

The part in bold is found for instance in net/http/server.go#Write():

// Write writes the headers described in h to w.
//
// This method has a value receiver, despite the somewhat large size
// of h, because it prevents an allocation. The escape analysis isn't
// smart enough to realize this function doesn't mutate h.
func (h extraHeader) Write(w *bufio.Writer) {
...
}


Note: irbull points out in the comments a warning about interface methods:

Following the advice that the receiver type should be consistent, if you have a pointer receiver, then your (p *type) String() string method should also use a pointer receiver.

But this does not implement the Stringer interface, unless the call

Code Snippets

// Write writes the headers described in h to w.
//
// This method has a value receiver, despite the somewhat large size
// of h, because it prevents an allocation. The escape analysis isn't
// smart enough to realize this function doesn't mutate h.
func (h extraHeader) Write(w *bufio.Writer) {
...
}

Context

Stack Overflow Q#27775376, score: 264

Revisions (0)

No revisions yet.