# Why Methods Matter
Methods attach behavior directly to data types, making APIs more discoverable and expressive. They are central to interface design in Go.
Most real bugs here come from receiver selection (value vs pointer), especially when mutation or interface satisfaction is involved.
Real-World Analogyclick to expand
If structs are nouns, methods are verbs. Defining verbs next to nouns keeps your domain model coherent and easy to reason about.
# Value vs Pointer Receivers
Value receivers work on copies; pointer receivers can mutate original state and avoid large copies.
type Rectangle struct{ W, H float64 }
func (r Rectangle) Area() float64 { return r.W * r.H }
func (r *Rectangle) Scale(f float64) {
r.W *= f
r.H *= f
}
rect := Rectangle{W: 4, H: 3}
rect.Scale(2)
fmt.Println(rect.Area()) // 48# Methods on Named Types
Methods are not limited to structs; you can define them on any named type in your package.
type Celsius float64
func (c Celsius) ToFahrenheit() float64 {
return float64(c)*9/5 + 32
}# Method Sets and Interfaces
Interface satisfaction depends on method sets. If any method needs pointer receiver semantics, stay consistent with pointer receivers across the type.
type Counter struct{ n int }
func (c *Counter) Inc() { c.n++ }
var c Counter
c.Inc()
fmt.Println(c.n) // 1# Practice Challenge
Implement a stack type with pointer-receiver mutation methods and a safe pop API.
package main
import "fmt"
type Stack struct {
items []int
}
func (s *Stack) Push(v int) { /* TODO */ }
func (s *Stack) Pop() (int, bool) { /* TODO */ }
func (s *Stack) Len() int { /* TODO */ }
func main() {
var s Stack
s.Push(1)
s.Push(2)
s.Push(3)
fmt.Println(s.Len())
}⚡ Key Takeaways
- Receiver choice controls mutation behavior and performance profile
- Use pointer receivers when state changes or struct copies are expensive
- Method sets determine interface satisfaction rules
- Methods can be defined on any named type in the same package