03 — Structs & Methods

How Go attaches behaviour to data: methods with receivers, the value-vs-pointer receiver choice, and embedding as composition. Every example is live — edit it and press Run.

1. Methods and the receiver choice

A method is a function with a receiver written before the name. In C you'd write a free function taking a Rect *; Go moves that argument to the front and calls it with dot syntax. The key decision is what the receiver gets:

package main

import "fmt"

type Rect struct {
    W, H float64
}

func (r Rect) Area() float64      { return r.W * r.H }        // value receiver: reads
func (r Rect) Perimeter() float64 { return 2 * (r.W + r.H) }

func (r *Rect) Scale(f float64) { // pointer receiver: mutates
    r.W *= f
    r.H *= f
}

func main() {
    r := Rect{W: 3, H: 4}
    fmt.Println(r.Area(), r.Perimeter())
    r.Scale(2)                      // Go takes the address automatically
    fmt.Println(r.W, r.H, r.Area())
}

Output:

12 14
6 8 48

You call both the same way: r.Scale(2) works because r is a variable, so Go rewrites it to (&r).Scale(2) for you.

2. Constructors and embedding

Go has no constructor keyword — the convention is a NewXxx function that returns a value (often a pointer). And instead of inheritance, Go has embedding: list a type with no field name and its fields and methods are promoted onto the outer struct. It's composition, not subclassing — no vtable, no super.

package main

import (
    "fmt"
    "math"
)

type Named struct {
    Name string
}

type Circle struct {
    Named          // embedded: Circle gets .Name directly
    R    float64
}

func NewCircle(name string, r float64) *Circle {
    return &Circle{Named: Named{Name: name}, R: r}
}

func (c Circle) Area() float64 { return math.Pi * c.R * c.R }

func (c Circle) Describe() string {
    return fmt.Sprintf("%s has area %.2f", c.Name, c.Area()) // c.Name is promoted
}

func main() {
    c := NewCircle("unit", 1)
    fmt.Println(c.Name)              // promoted from embedded Named
    fmt.Println(c.Describe())
    fmt.Println(NewCircle("big", 10).Describe())
}

Output:

unit
unit has area 3.14
big has area 314.16

Recap

ConceptIn Go
Methodfunc (r Rect) Area() float64, called r.Area()
Value receiverGets a copy; use for reads
Pointer receiverfunc (r *Rect) ...; mutates the caller
ConstructorConvention: func NewThing(...) *Thing
EmbeddingField with no name; promotes inner fields/methods (composition)

Next: 04 — Interfaces.