Value Receivers vs Pointer Receivers in Go (A Practical Explanation)
Source: Dev.to
Introduction
One of the first design questions you encounter in Go is whether a method should use a value receiver or a pointer receiver. Both forms appear frequently:
func (u User) Greet() {}
func (u *User) UpdateName() {}
Although they look similar, the choice impacts correctness, performance, and how your types behave in real backend code.
Value Receivers
In Go, a method is just a function attached to a type.
type User struct {
Name string
}
Adding behavior with a value receiver:
func (u User) Greet() {
fmt.Println("Hello,", u.Name)
}
The u here is called the receiver; it is a copy of the value on which the method is called.
user := User{Name: "Shivam"}
user.Greet()
Modifying a Value Receiver
When a method receives a copy, any modifications affect only that copy.
func (u User) ChangeName() {
u.Name = "New Name"
}
Example
package main
import "fmt"
type User struct {
Name string
}
func (u User) ChangeName() {
u.Name = "New Name"
}
func main() {
user := User{Name: "Shivam"}
user.ChangeName()
fmt.Println(user.Name) // Output: Shivam
}
The output remains Shivam because the method worked on a copy.
Key point: Value receiver = work on a copy.
When to Use Value Receivers
- The struct is small.
- The method does not need to modify the receiver (read‑only helpers).
A typical example is a Point type:
func (p Point) Distance() float64 { /* ... */ }
Pointer Receivers
A pointer receiver gives the method access to the original struct in memory.
func (u *User) ChangeName() {
u.Name = "New Name"
}
Example
package main
import "fmt"
type User struct {
Name string
}
func (u *User) ChangeName() {
u.Name = "New Name"
}
func main() {
user := User{Name: "Shivam"}
user.ChangeName()
fmt.Println(user.Name) // Output: New Name
}
The change persists because the method operated on the original object.
Key point: Pointer receiver = work on the original.
Advantages of Pointer Receivers
- State modification: Methods that need to update fields should almost always use a pointer receiver.
- Performance: Large structs avoid costly copies on each method call.
- Interface satisfaction: Methods with pointer receivers belong only to the pointer type, affecting whether a type satisfies an interface.
Choosing Between Value and Pointer Receivers
| Situation | Recommended Receiver |
|---|---|
| Method must modify the receiver’s fields | Pointer receiver (*T) |
| Struct is large and copying is expensive | Pointer receiver |
| Method only reads data and the struct is small | Value receiver (T) |
| You need the type to implement an interface that requires pointer methods | Pointer receiver |
In practice, for types that represent real entities in a backend system (services, handlers, configs, DB models, controllers), pointer receivers are the default choice. For simple value types that behave like immutable data, value receivers are fine.
Summary
- Value receiver: works on a copy; modifications are not reflected outside the method.
- Pointer receiver: works on the original; modifications persist.
- Use pointer receivers when you need to modify state, avoid copying large structs, or satisfy interfaces that require pointer methods.
- Use value receivers for small, read‑only types.
Once you internalize this distinction, designing methods in Go becomes straightforward.