Value vs Pointer Receivers in Go - Why It Matters for Interfaces
Go is a language that values simplicity, but sometimes this simplicity becomes confusing like in value vs pointer receivers.
Method Receiver
Methods are functions which are bounded to a type. They come with receiver which can be either value receiver or pointer receiver, value receiver gets a copy and pointer receiver gets a reference.
type Circle struct {
radius int
}
// Value receiver
func (c Circle) Area() int {
return c.radius * c.radius
}
// Pointer receiver
func (c *Circle) Scale(factor int) {
c.radius *= factor
}
Value Receiver (c Circle) |
Pointer Receiver (*c Circle) |
---|---|
Copies the value. | Uses the same value (reference). |
Can not modify the original. | Can modify the original struct. |
Works with both values and pointers. | Only works with pointers. |
Good for small structs, no mutation. | Good for large structs or needing mutation. |
Interfaces
We know that a type T implements an interface only if T has all the methods.
Here comes confusing part - If any method has a pointer receiver, T itself does not implement the interface, but *T does.
type Shape interface {
Area() int
Perimeter() int
}
type Circle struct {
radius int
}
// Mixed Receivers
func (c Circle) Area() int {
return c.radius * c.radius
}
func (c *Circle) Perimeter() int {
return 2 * c.radius
}
var s1 Shape = Circle{radius: 5} // Error
var s2 Shape = &Circle{radius: 5} // Works
Circle has Area, but not Perimeter because it's pointer receiver only.
*Circle has both.
According to Go Documentation we should pick one for all methods:
Recommendations
Use value receivers when:
- Your type is small
- Methods are read-only
Use pointer receivers when:
- Your type is large
- You modify the receiver
- Any method needs a pointer → make all pointer receivers for consistency
Cheers 👯♀️