In Golang the general recommendation is to use pointer receivers if there is a need to modify the data structure.

But then the question is can we still use value receivers even if we need to modify data?

There are examples where the need to modify shared data can be met without actually changing shared state. For example persistent data structures represent the idea of doing modifications without changing shared state.

Using persistent data structures versus shared state is about doing a trade off analysis. Which one do we need most:

  • strong thread safety guarantees

  • fast execution speed

If we want the application to execute fast then we would probably prefer shared state. But why not have them both at the same time? Golang is fast and provides built-in thread safety mechanisms.

For most cases the benefits of using persistent data structures when building concurrent applications outweigh the downside of the performance hit.

On the other hand there are programming languages like Haskell where the concept of a shared state has its own representation mechanism. The corner stone of Haskell is the idea of immutability.

I prefer to stick to the immutability idea as much as possible even in Golang. When I do Golang development I’m thinking about reducing shared state as much as possible. I prefer to use value receivers instead of pointer receivers. This is not always possible or reasonable.

In general these are the rules in Golang regarding pointer vs value receivers:

  • Need to mutate state? - use pointer receiver

  • Consistency - if some of the methods of the type are pointer receivers already then for the sake of consistency use pointer receiver

  • If the receiver is a map, func or chan, don’t use a pointer because these types are pointers already

  • If the receiver is a struct that contains sync.Mutex or similar fields then the receiver must be a pointer in order to avoid copying of the synchronization fields

  • Use value receiver for small data structures with no mutable fields or pointer fields

  • Use value receiver for basic types (int, string…)