Receiver Functions in Go
What Are Receiver Functions?
Receiver functions allow you to define methods on types. They’re Go’s way of attaching functionality to structs (or any user-defined type).
type User struct {
Name string
Age int
}
// Receiver function
func (u User) Greet() string {
return fmt.Sprintf("Hello, %s!", u.Name)
}Basic Syntax
func (receiverName ReceiverType) MethodName(parameters) returnTypes {
// Method body
}Value vs Pointer Receivers
| Feature | Value Receiver (u User) | Pointer Receiver (u *User) |
|---|---|---|
| Modifies original | ❌ No | ✅ Yes |
| Memory Usage | Copies entire value | Uses reference (4/8 bytes) |
| Best For | Read-only operations | Mutating operations |
| Nil Safety | Always safe | May cause panics |
// Value receiver (read-only)
func (u User) GetAge() int {
return u.Age
}
// Pointer receiver (can modify)
func (u *User) Birthday() {
u.Age++
}FAQ: Common Interview Questions
1. When to use pointer receivers?
A: Use when:
- You need to modify the receiver
- Working with large structs (avoid copying)
- Implementing interface methods that require mutation
2. Can any type have methods?
A: Yes, but with restrictions:
type MyInt int
func (m MyInt) Double() int {
return int(m * 2)
}Exception: Cannot define methods on:
- Built-in types directly (must alias)
- Pointers directly
- Interfaces
3. What’s the convention for receiver names?
A: Typically 1-2 letters matching type:
uforUserpforProductcforClient
4. Can interfaces have receiver methods?
A: No, interfaces define method signatures but can’t implement them.
5. Can built-in types have methods?
A: Only via type aliasing:
type Celsius float64
func (c Celsius) ToFahrenheit() float64 {
return float64(c)*9/5 + 32
}6. What’s the zero-value receiver behavior?
A: Methods can be called on nil receivers if properly handled:
func (a *Account) Balance() float64 {
if a == nil {
return 0.0
}
return a.balance
}7. How does Go handle method calls on values vs pointers?
A: Go automatically converts:
value.Method()works for both value and pointer receiverspointer.Method()works for both receiver types
Practical Patterns
Method Chaining
type Calculator struct { Value float64 }
func (c *Calculator) Add(x float64) *Calculator {
c.Value += x
return c
}
func (c *Calculator) Multiply(x float64) *Calculator {
c.Value *= x
return c
}
// Usage:
result := new(Calculator).Add(5).Multiply(2).Value // 10Interface Implementation
type Speaker interface { Speak() string }
type Dog struct{}
func (d Dog) Speak() string { return "Woof!" }
// Dog implicitly implements SpeakerNil Receiver Handling
func (u *User) SafeGreet() string {
if u == nil {
return "Hello, guest!"
}
return fmt.Sprintf("Hello, %s", u.Name)
}
// Usage:
var emptyUser *User
fmt.Println(emptyUser.SafeGreet()) // "Hello, guest!"Common Pitfalls & Solutions
-
Inconsistent Receiver Types
// Avoid mixing receiver types for the same struct type Product struct{} func (p Product) GetPrice() float64 {...} // Value func (p *Product) SetPrice() {...} // Pointer -
Accidental Value Copies
func (u User) UpdateName() { u.Name = "Changed" // Doesn't modify original! } -
Nil Pointer Panics
var u *User u.Birthday() // Panic! -
Consistency Matters
- Choose one receiver type (value or pointer) per struct
- Exception: When specific methods need mutation capability
-
Receiver Naming
- Keep short (1-2 chars) and type-related:
uforUsercforClientdbforDatabase
- Keep short (1-2 chars) and type-related:
-
Size Considerations
// Use pointer receivers for large structs type BigData struct { data [1_000_000]int } func (b *BigData) Process() {...} // Better for memory
Memory Aid
- Value receiver = View-only
- Pointer receiver = Permission to modify
(t T)= “Take a copy”(t *T)= “Take the original”