Methods
Methods in Go are functions with a special receiver argument. They allow you to define behavior on types, enabling object-oriented programming patterns while maintaining Go's simplicity.
Key Points
- Methods are functions with a receiver parameter
- Receivers can be value receivers or pointer receivers
- Value receivers operate on copies, pointer receivers can modify original
- Methods can be defined on any type in the same package
- Method names must be unique per type
- Use pointer receivers for large structs to avoid copying
- Methods enable types to satisfy interfaces
- Cannot define methods on built-in types directly
Defining Methods
Methods are defined with a receiver between func and the method name:
package main
import (
"fmt"
"math"
)
type Rectangle struct {
Width float64
Height float64
}
type Circle struct {
Radius float64
}
// Method with value receiver
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
// Method with value receiver
func (r Rectangle) Perimeter() float64 {
return 2 * (r.Width + r.Height)
}
// Method with value receiver
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
func main() {
rect := Rectangle{Width: 10, Height: 5}
circ := Circle{Radius: 7}
fmt.Printf("Rectangle area: %.2f\n", rect.Area())
fmt.Printf("Rectangle perimeter: %.2f\n", rect.Perimeter())
fmt.Printf("Circle area: %.2f\n", circ.Area())
}Rectangle area: 50.00
Rectangle perimeter: 30.00
Circle area: 153.94Pointer vs Value Receivers
Pointer receivers allow methods to modify the receiver:
package main
import "fmt"
type Counter struct {
Count int
}
// Value receiver - operates on a copy
func (c Counter) IncrementValue() {
c.Count++
fmt.Println("Inside IncrementValue:", c.Count)
}
// Pointer receiver - modifies original
func (c *Counter) IncrementPointer() {
c.Count++
fmt.Println("Inside IncrementPointer:", c.Count)
}
// Pointer receiver for reading is also common
func (c *Counter) GetCount() int {
return c.Count
}
func main() {
counter := Counter{Count: 0}
fmt.Println("Initial count:", counter.Count)
// Value receiver doesn't modify original
counter.IncrementValue()
fmt.Println("After IncrementValue:", counter.Count)
// Pointer receiver modifies original
counter.IncrementPointer()
fmt.Println("After IncrementPointer:", counter.Count)
// Go automatically takes address when needed
counter.IncrementPointer()
fmt.Println("Final count:", counter.GetCount())
}Initial count: 0
Inside IncrementValue: 1
After IncrementValue: 0
Inside IncrementPointer: 1
After IncrementPointer: 1
Inside IncrementPointer: 2
Final count: 2Methods on Custom Types
Define methods on any type defined in your package:
package main
import (
"fmt"
"strings"
)
// Custom type based on string
type Name string
// Method on custom string type
func (n Name) Greeting() string {
return fmt.Sprintf("Hello, %s!", n)
}
// Method with pointer receiver
func (n *Name) Uppercase() {
*n = Name(strings.ToUpper(string(*n)))
}
// Custom type based on slice
type IntSlice []int
// Method on slice type
func (is IntSlice) Sum() int {
total := 0
for _, v := range is {
total += v
}
return total
}
func main() {
name := Name("Alice")
fmt.Println(name.Greeting())
name.Uppercase()
fmt.Println(name.Greeting())
numbers := IntSlice{1, 2, 3, 4, 5}
fmt.Println("Sum:", numbers.Sum())
}Hello, Alice!
Hello, ALICE!
Sum: 15Method Syntax
| Pattern | Example | Description |
|---|---|---|
| Value receiver | func (r Rect) Area() float64 | Operates on copy of receiver |
| Pointer receiver | func (r *Rect) Scale(f float64) | Can modify receiver |
| Method call | obj.Method() | Call method on object |
| Method value | f := obj.Method | Store method as function value |
| Method expression | f := Type.Method | Get method as function |
When to Use Pointer Receivers
| Scenario | Receiver Type | Reason |
|---|---|---|
| Need to modify receiver | Pointer | Value receivers operate on copies |
| Large struct | Pointer | Avoid copying overhead |
| Consistency | Pointer | If some methods use pointer, use for all |
| Small struct, read-only | Value | Simple and safe |
| Built-in types | Value or Pointer | Depends on use case |
| Interface implementation | Consider both | Value receiver more flexible |
Method Characteristics
| Feature | Description | Notes |
|---|---|---|
| Auto-dereferencing | Go automatically dereferences pointers | ptr.Method() works automatically |
| Auto-addressing | Go takes address when needed | value.PointerMethod() works |
| Package restriction | Methods must be in same package as type | Cannot add methods to external types |
| No overloading | Cannot have multiple methods with same name | Even with different parameters |
| Interface satisfaction | Methods enable interface implementation | Implicit interface satisfaction |
| Visibility | Uppercase = exported, lowercase = unexported | Same rules as functions |
Common Patterns
| Pattern | Example | Use Case |
|---|---|---|
| Getter method | func (p *Person) Name() string | Access private fields |
| Setter method | func (p *Person) SetName(n string) | Modify with validation |
| Fluent interface | func (b *Builder) Add() *Builder | Method chaining |
| String representation | func (p Person) String() string | Implement fmt.Stringer |
| Validation | func (u *User) Validate() error | Check object validity |
| State modification | func (s *Server) Start() error | Change object state |