GoGo · Lesson 6 of 8

Structs & Interfaces

Go favors composition over inheritance. There are no classes, no extends. Just structs, methods, and interfaces. It's refreshingly simple.

Go
package main

import (
    "fmt"
    "math"
)

// Struct definition
type Point struct {
    X, Y float64
}

// Method on a struct (pointer receiver enables mutation)
func (p *Point) Scale(factor float64) {
    p.X *= factor
    p.Y *= factor
}

// Value receiver (doesn't mutate)
func (p Point) Distance() float64 {
    return math.Sqrt(p.X*p.X + p.Y*p.Y)
}

func (p Point) String() string {
    return fmt.Sprintf("(%.2f, %.2f)", p.X, p.Y)
}

// Embedding (composition)
type ColoredPoint struct {
    Point          // embed Point — gets all its methods
    Color string
}

// Interface — any type with these methods satisfies it
type Shape interface {
    Area() float64
    Perimeter() float64
}

type Circle struct{ Radius float64 }
type Rect struct{ Width, Height float64 }

func (c Circle) Area() float64 { return math.Pi * c.Radius * c.Radius }
func (c Circle) Perimeter() float64 { return 2 * math.Pi * c.Radius }
func (r Rect) Area() float64 { return r.Width * r.Height }
func (r Rect) Perimeter() float64 { return 2 * (r.Width + r.Height) }

func printShapeInfo(s Shape) {
    fmt.Printf("Area: %.2f, Perimeter: %.2f\n", s.Area(), s.Perimeter())
}

func main() {
    p := Point{3, 4}
    fmt.Println(p.Distance())   // 5

    p.Scale(2)
    fmt.Println(p)              // (6.00, 8.00)

    cp := ColoredPoint{Point: Point{1, 2}, Color: "red"}
    fmt.Println(cp.Distance())  // 2.23... (method promoted from Point)

    shapes := []Shape{
        Circle{Radius: 5},
        Rect{Width: 4, Height: 6},
    }
    for _, s := range shapes {
        printShapeInfo(s)
    }
}
◆ Note
In Go, interfaces are satisfied implicitly — a type doesn't declare that it implements an interface. If it has the right methods, it satisfies the interface. This is called structural typing and keeps code decoupled.