In Go, methods can be defined on either values or pointers. This choice affects how the method interacts with the receiver, including whether it can modify the original value and performance considerations.
Key Differences
- Value Receivers: When a method is defined on a value (e.g.,
func (part Part) Method()), it receives a copy of the value. Changes inside the method do not affect the original value. For large structs, this can be inefficient due to copying. - Pointer Receivers: When a method is defined on a pointer (e.g.,
func (part *Part) Method()), it receives a pointer to the value. Changes can modify the original value, and it’s more efficient for large structs since only the pointer is passed.
Regarding method sets: Regardless of whether the receiver is a value or a pointer, you can call methods on both values and pointers without special handling. Go automatically handles referencing and dereferencing.
Example Code
package main
import (
"fmt"
"strings"
)
type Part struct {
Id int
Name string
}
func (part *Part) LowerCase() {
part.Name = strings.ToLower(part.Name)
}
func (part *Part) UpperCase() {
part.Name = strings.ToUpper(part.Name)
}
func (part Part) String() string {
return fmt.Sprintf("<<%d %q>>", part.Id, part.Name)
}
func (part Part) HasPrefix(prefix string) bool {
return strings.HasPrefix(part.Name, prefix)
}
func main() {
// Using a pointer receiver
p := &Part{1, "this is a new part"}
p.UpperCase()
fmt.Println(p)
fmt.Println(p.HasPrefix("prefix"))
// Using a value receiver
var p1 Part
p1 = Part{2, "this is part 2"}
p1.UpperCase() // Go automatically takes the address for pointer receiver
fmt.Println(p1)
p1.LowerCase()
fmt.Println(p1)
fmt.Println(p1.HasPrefix(""))
}
Output:
<<1 "THIS IS A NEW PART">>
false
<<2 "THIS IS PART 2">>
<<2 "this is part 2">>
true
Important Points
- Automatic Conversion: Go allows calling pointer receiver methods on values and vice versa. For example,
p1.UpperCase()works even thoughUpperCasehas a pointer receiver; Go takes the address ofp1. - Consistency: If a type has methods with pointer receivers, it’s conventional to use pointer receivers for all methods to avoid confusion.
- Performance: Use pointer receivers for large structs or when modifying the receiver. Use value receivers for small structs or when immutability is desired.
- Interfaces: Methods with pointer receivers can only be called on pointers that implement the interface, while value receiver methods can be called on both.
- Best Practices: Prefer pointer receivers unless there’s a good reason to use value receivers (e.g., for immutability or small types like
int).
Conclusion
Choosing between value and pointer receivers depends on whether you need to modify the receiver and performance considerations. Understanding this helps write efficient and idiomatic Go code.