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:
- a value receiver
(r Rect)gets a copy — fine for reads, can't change the caller's value; - a pointer receiver
(r *Rect)can mutate the original, like a CRect *.
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
| Concept | In Go |
|---|---|
| Method | func (r Rect) Area() float64, called r.Area() |
| Value receiver | Gets a copy; use for reads |
| Pointer receiver | func (r *Rect) ...; mutates the caller |
| Constructor | Convention: func NewThing(...) *Thing |
| Embedding | Field with no name; promotes inner fields/methods (composition) |
Next: 04 — Interfaces.