thecodex.expert · The Codex Family of Knowledge
Go

Structs and Methods

Go has no classes — a struct groups data, and a method is just a function with an extra receiver argument bound to that struct’s type.

Go 1.26 No classes Last verified:
Canonical Definition

A struct is a composite type that groups together zero or more named fields of any type. Go has no class keyword; instead, a method is declared as an ordinary function with an extra receiver parameter before its name, which binds that function to a specific named type. The receiver can be a value (func (p Point) Method()) or a pointer (func (p *Point) Method()), and the choice changes whether the method can mutate the original data.

Declaring and using structs

A struct groups named fields under one type. Struct values can be created with field names (recommended, order-independent) or positionally (order-dependent and brittle if the struct changes), and fields are accessed with dot notation.

Gostructs.go
type Point struct {
    X, Y int
}

p1 := Point{X: 3, Y: 4}   // named fields — preferred
p2 := Point{3, 4}          // positional — works, but brittle

p1.X = 10                  // dot notation to access/set fields
fmt.Println(p1.X, p1.Y)    // 10 4

Methods: functions with a receiver

A method looks like a regular function, but names a receiver type in parentheses before the function name. That receiver is how Go attaches behavior to a type without a class hierarchy — any named type, not just structs, can have methods.

Gomethods.go
type Rectangle struct {
    Width, Height float64
}

func (r Rectangle) Area() float64 {   // value receiver
    return r.Width * r.Height
}

rect := Rectangle{Width: 3, Height: 4}
fmt.Println(rect.Area())   // 12

Value receivers vs. pointer receivers

This is the detail that trips up newcomers most: a method with a value receiver operates on a copy of the struct, so changes inside the method never affect the original. A method with a pointer receiver (*Rectangle) operates on the original data, so mutations are visible to the caller. Go automatically takes the address when you call a pointer-receiver method on an addressable value, so the call syntax looks identical either way — the difference is entirely in the method's declaration, not the call site. The idiomatic rule: if any method on a type needs a pointer receiver (to mutate, or to avoid copying a large struct), make all of that type's methods use pointer receivers, for consistency.

Goreceivers.go
func (r Rectangle) Scale(factor float64) {    // value receiver: no effect outside
    r.Width *= factor
}

func (r *Rectangle) ScalePtr(factor float64) { // pointer receiver: mutates original
    r.Width *= factor
}

rect := Rectangle{Width: 3, Height: 4}
rect.Scale(2)
fmt.Println(rect.Width)     // still 3 — Scale worked on a copy

rect.ScalePtr(2)
fmt.Println(rect.Width)     // 6 — ScalePtr mutated the original

Sources

1
The Go Authors. The Go Programming Language Specification, "Method declarations", and "A Tour of Go" — Methods section, go.dev/ref/spec & go.dev/tour.