>_
GolangStepByStep
Software Engineer

Structs & Interfaces

Compose types and define behavior with interfaces

# Start With the Mental Model

Structs hold data. Methods add behavior. Interfaces describe behavior. Embedding lets you reuse behavior.

  • Struct = data shape
  • Method = behavior on that data
  • Interface = contract (behavior only)
  • Embedding = composition and reuse

# Structs

Structs group fields together. The zero value of a struct is usable immediately (all fields are zeroed).

type User struct {
    Name  string
    Email string
    Age   int
}

u1 := User{Name: "Alice", Email: "alice@go.dev", Age: 30}
var u2 User // zero value
fmt.Println(u2.Name, u2.Age) // "", 0

# Methods and Receivers

Methods are functions with receivers. Value receivers operate on a copy. Pointer receivers can mutate.

type Counter struct { count int }

func (c *Counter) Inc() { c.count++ }
func (c Counter) Value() int { return c.count }

c := Counter{}
c.Inc()
fmt.Println(c.Value())

Use pointer receivers when you need mutation or want to avoid copying large structs.

# Interfaces

Interfaces describe behavior. Any type that has the methods automatically satisfies the interface.

type Shape interface {
    Area() float64
}

type Rect struct { W, H float64 }
func (r Rect) Area() float64 { return r.W * r.H }

func printArea(s Shape) {
    fmt.Println(s.Area())
}

Small interfaces are easier to test and reuse. Define interfaces where they are used, not where they are implemented.

# The Nil Interface Trap

An interface is nil only if both its type and value are nil. A typed nil pointer inside an interface is not nil.

type MyErr struct{}
func (e *MyErr) Error() string { return "boom" }

func f() error {
    var e *MyErr = nil
    return e // NOT nil interface
}

Fix by returning a literal `nil` instead of a typed nil.

# Embedding (Composition)

Embedding promotes fields and methods from the embedded type to the outer type. This is composition, not inheritance.

type Logger struct{}
func (Logger) Log(msg string) {}

type Server struct {
    Logger // embedded
}

s := Server{}
s.Log("hi")

⚡ Key Takeaways

  • Structs hold data; methods add behavior.
  • Pointer receivers enable mutation and avoid large copies.
  • Interfaces are satisfied implicitly, so design small interfaces.
  • Embedding is composition and promotes methods.
  • Nil interface is a common pitfall — type and value must be nil.
practice & review