>_ Golang Step By Step
Software Engineer

Structs & Interfaces

Compose types and define behavior with interfaces

# Defining Structs

Structs group related data together. They're Go's primary way to create custom types — there are no classes.

type User struct {
    Name  string
    Email string
    Age   int
}

func main() {
    // Named fields
    u1 := User{Name: "Alice", Email: "alice@go.dev", Age: 30}

    // Pointer to struct
    u2 := &User{Name: "Bob", Age: 25}

    fmt.Println(u1.Name)  // Alice
    fmt.Println(u2.Name)  // Bob (auto-dereferenced)
}

# Methods

Methods are functions with a receiver. Use a pointer receiver *T to modify the struct:

type Rect struct {
    Width, Height float64
}

// Value receiver — doesn't modify
func (r Rect) Area() float64 {
    return r.Width * r.Height
}

// Pointer receiver — can modify
func (r *Rect) Scale(factor float64) {
    r.Width *= factor
    r.Height *= factor
}

func main() {
    r := Rect{Width: 3, Height: 4}
    fmt.Println(r.Area()) // 12
    r.Scale(2)
    fmt.Println(r.Area()) // 48
}

# Interfaces — Implicit Contracts

Interfaces define behavior, not data. A type satisfies an interface simply by implementing its methods — noimplements keyword needed.

type Shape interface {
    Area() float64
    Perimeter() float64
}

type Circle struct {
    Radius float64
}

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

func (c Circle) Perimeter() float64 {
    return 2 * math.Pi * c.Radius
}

// Circle implicitly satisfies Shape!
func printInfo(s Shape) {
    fmt.Printf("Area: %.2f, Perimeter: %.2f\n",
        s.Area(), s.Perimeter())
}

# Struct Embedding (Composition)

Go prefers composition over inheritance. Embed one struct inside another to reuse its fields and methods:

type Animal struct {
    Name string
}

func (a Animal) Speak() string {
    return a.Name + " makes a sound"
}

type Dog struct {
    Animal       // embedded (no field name)
    Breed string
}

func main() {
    d := Dog{
        Animal: Animal{Name: "Rex"},
        Breed: "Labrador",
    }
    fmt.Println(d.Name)    // promoted: Rex
    fmt.Println(d.Speak()) // promoted: Rex makes a sound
}

⚡ Key Takeaways

  • Go has structs, not classes — behavior is added via methods with receivers
  • Use pointer receivers *T when mutating or for large structs
  • Interfaces are satisfied implicitly — no implements keyword
  • Embedding promotes fields & methods — composition over inheritance
  • Keep interfaces small (1-3 methods) — Go's stdlib follows this religiously
practice & review