Go Gopher How to Go

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())
}
Output:
Rectangle area: 50.00
Rectangle perimeter: 30.00
Circle area: 153.94

Pointer 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())
}
Output:
Initial count: 0
Inside IncrementValue: 1
After IncrementValue: 0
Inside IncrementPointer: 1
After IncrementPointer: 1
Inside IncrementPointer: 2
Final count: 2

Methods 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())
}
Output:
Hello, Alice!
Hello, ALICE!
Sum: 15

Method Syntax

PatternExampleDescription
Value receiverfunc (r Rect) Area() float64Operates on copy of receiver
Pointer receiverfunc (r *Rect) Scale(f float64)Can modify receiver
Method callobj.Method()Call method on object
Method valuef := obj.MethodStore method as function value
Method expressionf := Type.MethodGet method as function

When to Use Pointer Receivers

ScenarioReceiver TypeReason
Need to modify receiverPointerValue receivers operate on copies
Large structPointerAvoid copying overhead
ConsistencyPointerIf some methods use pointer, use for all
Small struct, read-onlyValueSimple and safe
Built-in typesValue or PointerDepends on use case
Interface implementationConsider bothValue receiver more flexible

Method Characteristics

FeatureDescriptionNotes
Auto-dereferencingGo automatically dereferences pointersptr.Method() works automatically
Auto-addressingGo takes address when neededvalue.PointerMethod() works
Package restrictionMethods must be in same package as typeCannot add methods to external types
No overloadingCannot have multiple methods with same nameEven with different parameters
Interface satisfactionMethods enable interface implementationImplicit interface satisfaction
VisibilityUppercase = exported, lowercase = unexportedSame rules as functions

Common Patterns

PatternExampleUse Case
Getter methodfunc (p *Person) Name() stringAccess private fields
Setter methodfunc (p *Person) SetName(n string)Modify with validation
Fluent interfacefunc (b *Builder) Add() *BuilderMethod chaining
String representationfunc (p Person) String() stringImplement fmt.Stringer
Validationfunc (u *User) Validate() errorCheck object validity
State modificationfunc (s *Server) Start() errorChange object state