Go (Golang) Tutorial 0/45 lessons ~6 min read Lesson 16

    Methods

    Methods are functions with a receiver — a special parameter before the function name that binds the function to a type.

    Course progress0%
    Focus
    10 guided sections
    Practice signal
    Examples included
    Career prep
    Interview Q&A included

    Introduction

    Methods are functions with a receiver — a special parameter before the function name that binds the function to a type. Receivers can be value or pointer; pointer receivers are required when methods mutate state or when the struct is large.

    Go does not have classes — methods can be defined on any type in the same package, including non-struct types like custom int aliases. Method sets determine which methods a type satisfies for interfaces — pointer vs value receiver affects interface satisfaction.

    This lesson covers receiver choice, method expressions, and the idiomatic patterns used in the standard library (http.Server methods, bytes.Buffer Write).

    The story

    A Cloudflare rate-limiter attaches behavior to a Limiter struct: func (l *Limiter) Allow(key string) bool. Pointer receivers mutate internal token buckets; value receivers suit immutable value types. Kubernetes' client-go defines methods on typed clients — the pattern you'll implement daily in backend services.

    Choosing pointer vs value receiver consistently across a type prevents subtle bugs when interface satisfaction and mutation expectations diverge.

    Understanding the topic

    Key concepts

    • func (r ReceiverType) MethodName(params) return — value receiver.
    • func (r *ReceiverType) MethodName — pointer receiver for mutation.
    • Method set: value type has value methods; pointer type has both value and pointer methods.
    • Methods on non-struct types: type MyInt int; func (m MyInt) Double() int.
    • No method overloading — one method name per receiver type.
    • Interface satisfaction uses method set of the type used.

    Step-by-step explanation

    1. Compiler desugars method call: v.Method(x) → Method(v, x).
    2. Value receiver copies struct — mutations don't persist.
    3. Pointer receiver shares instance — mutations visible.
    4. Go auto-dereferences: ptr.Method() works for pointer receiver.
    5. Method expression: f := Type.Method; f(instance, args).
    6. Consistency: if any method needs pointer receiver, use pointer for all.

    Practical code example

    Bank account with pointer receiver for mutation and value receiver for read:

    go
    package main
    import "fmt"
    type Account struct {
    owner string
    balance float64
    }
    func NewAccount(owner string) *Account {
    return &Account{owner: owner}
    }
    func (a *Account) Deposit(amount float64) {
    if amount > 0 {
    a.balance += amount
    }
    }
    func (a *Account) Withdraw(amount float64) error {
    if amount > a.balance {
    return fmt.Errorf("insufficient funds")
    }
    a.balance -= amount
    return nil
    }
    func (a Account) Owner() string {
    return a.owner
    }
    func main() {
    acc := NewAccount("Alice")
    acc.Deposit(500)
    _ = acc.Withdraw(100)
    fmt.Printf("%s balance: %.2f\n", acc.Owner(), acc.balance)
    }

    Line-by-line code explanation

    • func (l *Limiter) Allow(key string) bool declares a method with a pointer receiver.
    • l.tokens-- mutates receiver state — only possible with pointer receivers.
    • func (p Point) Distance() float64 uses a value receiver for small immutable types.
    • (&l).Allow(key) Go auto-takes address when calling pointer methods on values.
    • method sets must be consistent — don't mix pointer and value receivers on the same type.
    • type Handler interface { ServeHTTP(...) } interfaces are satisfied implicitly by implementing methods.
    • func NewLimiter(r rate) *Limiter constructor returns a pointer ready for method calls.
    • method expressions: Limiter.Allow extracts a method as a function value.

    Key takeaway: Use pointer receiver when mutating or struct is large. Be consistent within a type. Constructor returns *Account for method calls.

    Real-world use

    Where you'll use this in production

    • Domain model behavior: order.Place(), account.Withdraw().
    • HTTP handlers as methods on controller struct with dependencies.
    • Custom types with validation: Email.Validate().
    • Builder pattern: cfg.WithTimeout(d).WithRetries(n).

    Best practices

    • Use pointer receiver for mutation and large structs.
    • Be consistent — all methods on a type use same receiver kind.
    • Keep methods on domain types; free functions for stateless ops.
    • Avoid getter/setter boilerplate — export fields when appropriate.
    • Document exported methods with godoc.

    Common mistakes

    • Value receiver on mutating method — changes lost.
    • Mixing value and pointer receivers — confusing interface satisfaction.
    • Nil pointer receiver call — panics if method doesn't nil-check.
    • Too many methods on one type — split responsibilities.
    • Method on interface type — not allowed, only on concrete/named types.

    Advanced interview questions

    Q1BeginnerValue vs pointer receiver?
    Value copies; pointer mutates original and avoids copy of large struct.
    Q2BeginnerCan methods be on any type?
    Any named type in same package except pointer or interface type.
    Q3IntermediateMethod set and interfaces?
    T satisfies interface with value methods; *T satisfies value+pointer methods.
    Q4IntermediateWhen nil pointer receiver is OK?
    When method nil-checks: if a == nil { return default } — rare pattern.
    Q5AdvancedDesign FileStore with Write, Read, Close methods.
    type FileStore struct { path string; f *os.File }; pointer receivers; idempotent Close; constructor opens file.

    Summary

    Methods attach behavior to types via value or pointer receivers. Pointer receivers for mutation; be consistent across methods. Method set determines interface satisfaction. Constructors return pointers for method call convenience. Next lesson: interfaces — Go's polymorphism mechanism.

    Ready to mark this lesson complete?Track your journey across the entire course.