Go Structs & Methods Cheat Sheet
Explains defining Go structs, value versus pointer method receivers, struct embedding for composition, and how Go achieves reuse without inheritance.
Structs
Declaring and initializing struct values.
type Point struct { X, Y int}p := Point{X: 3, Y: 4} // Named fieldsp2 := Point{5, 6} // Positional (must set all fields, in order)p3 := Point{} // Zero value: X=0, Y=0fmt.Println(p.X, p.Y)
Methods with Value & Pointer Receivers
Choosing between copying and mutating the receiver.
func (p Point) Distance() float64 { // Value receiver: operates on a copy return math.Sqrt(float64(p.X*p.X + p.Y*p.Y))}func (p *Point) Scale(factor int) { // Pointer receiver: mutates the original p.X *= factor p.Y *= factor}p := Point{X: 3, Y: 4}fmt.Println(p.Distance()) // 5p.Scale(2) // Go automatically takes &p for pointer receiversfmt.Println(p) // {6 8}
Embedding (Composition)
Reusing fields and methods without classical inheritance.
type Base struct { ID int}func (b Base) Describe() string { return fmt.Sprintf("ID=%d", b.ID)}type User struct { Base // Embedded struct -- promotes Base's fields and methods Name string}u := User{Base: Base{ID: 1}, Name: "Alice"}fmt.Println(u.ID) // Promoted field, accessible directlyfmt.Println(u.Describe()) // Promoted method
Concepts
Key rules for structs and their method sets.
- Zero value- A struct's fields default to their type's zero value if not explicitly initialized
- Value vs pointer receiver- Pointer receivers avoid copying and allow mutation; use them consistently across a type's methods
- Struct embedding- Go's mechanism for composition; the embedded type's fields/methods are "promoted" to the outer type
- No inheritance- Go has no classical inheritance -- embedding plus interfaces achieve similar reuse
- Struct comparison- Structs are comparable with == if all of their fields are comparable
- Struct tags- `json:"name"` metadata used by encoding/json and other reflection-based libraries
- Anonymous structs- x := struct{ A int }{A: 1} defines a one-off, unnamed struct type
Compile-Time Interface Satisfaction
Catching a missing method at build time instead of at a runtime type assertion.
type Stringer interface { String() string}type Point struct{ X, Y int }func (p Point) String() string { return fmt.Sprintf("(%d, %d)", p.X, p.Y)}// Blank identifier assignment forces a compile error if Point stops// satisfying Stringer -- a common pattern near the type definition.var _ fmt.Stringer = Point{}var _ fmt.Stringer = (*Point)(nil) // Verify the pointer type too
Interface Embedding & Method Promotion Conflicts
Composing interfaces, and what happens when embedded types collide.
type Reader interface{ Read(p []byte) (n int, err error) }type Writer interface{ Write(p []byte) (n int, err error) }type ReadWriter interface { // Interface embedding: union of method sets Reader Writer}type A struct{}func (A) Name() string { return "A" }type B struct{}func (B) Name() string { return "B" }type C struct { A B}// c.Name() is a COMPILE ERROR: ambiguous selector at depth 1 from both A and B.// Must disambiguate explicitly:func (c C) Name() string { return c.A.Name() }
Functional Options Pattern
Idiomatic Go alternative to constructors with many optional parameters.
type Server struct { addr string timeout time.Duration tls bool}type Option func(*Server)func WithTimeout(d time.Duration) Option { return func(s *Server) { s.timeout = d }}func WithTLS() Option { return func(s *Server) { s.tls = true }}func NewServer(addr string, opts ...Option) *Server { s := &Server{addr: addr, timeout: 30 * time.Second} // Sensible defaults for _, opt := range opts { opt(s) } return s}// srv := NewServer("localhost:8080", WithTLS(), WithTimeout(5*time.Second))
Gotchas & Deeper Rules
Behavior around embedding, method sets, and struct layout that trips up intermediate Go developers.
- Method set of *T- Includes both value- and pointer-receiver methods; the method set of T includes only value-receiver methods -- this is why *T satisfies more interfaces
- Embedded interface fields- A struct can embed an interface, not just a concrete type, letting it satisfy a larger interface while delegating most methods to the embedded value
- Shallow copy on assignment- Struct assignment/passing copies all fields; embedded pointer fields still point at shared data, but embedded structs are fully duplicated
- Struct alignment & padding- Field order affects size due to memory alignment; ordering large-to-small fields can reduce padding (check with unsafe.Sizeof)
- Method values vs method expressions- p.Method is a bound method value (receiver captured); Type.Method is a method expression taking the receiver as the first explicit argument
- Nil pointer receiver methods- A method with a pointer receiver can still be called on a nil *T if the method body never dereferences it -- useful for nil-safe tree/list types
- Unexported embedded types- Embedding an unexported type from another package still promotes its exported methods, a common trick for restricted extensibility
Be consistent with receiver types across a struct's method set -- mixing value and pointer receivers can cause a type to unexpectedly fail to satisfy an interface, since only *T gets the pointer-receiver methods.