04 — Interfaces

Describing behaviour abstractly: interfaces, implicit satisfaction, the any type, and recovering a concrete type safely with a type switch. Every example is live — edit it and press Run.

1. Interfaces are satisfied implicitly

An interface lists methods. A type satisfies it just by having those methods — there's no implements keyword and no declared link. Here both Dog and Cat satisfy Speaker simply by having a Speak() method, so you can treat them uniformly through the interface:

package main

import "fmt"

type Speaker interface {
    Speak() string
}

type Dog struct{}
type Cat struct{}

func (Dog) Speak() string { return "Woof" }   // Dog now satisfies Speaker
func (Cat) Speak() string { return "Meow" }   // so does Cat

func AllSounds(ss []Speaker) []string {
    out := make([]string, len(ss))
    for i, s := range ss {
        out[i] = s.Speak()                     // dispatches to the concrete type
    }
    return out
}

func main() {
    fmt.Println(AllSounds([]Speaker{Dog{}, Cat{}, Dog{}}))
}

Output:

[Woof Meow Woof]

2. any and the type switch

any (an alias for interface{}) holds a value of any type — like C's void *, but it remembers the concrete type, so you recover it safely. A type switch branches on what's really inside. Order matters: a value takes the first matching case, so list concrete types before a broader interface case.

package main

import "fmt"

type Speaker interface{ Speak() string }
type Dog struct{}

func (Dog) Speak() string { return "Woof" }

func Describe(x any) string {
    switch v := x.(type) {
    case int:
        return fmt.Sprintf("int: %d", v)
    case string:
        return fmt.Sprintf("string: %s", v)
    case Speaker:                          // checked after the concrete cases
        return fmt.Sprintf("speaker: %s", v.Speak())
    default:
        return "unknown"
    }
}

func main() {
    fmt.Println(Describe(42))
    fmt.Println(Describe("hi"))
    fmt.Println(Describe(Dog{}))
    fmt.Println(Describe(3.14))
}

Output:

int: 42
string: hi
speaker: Woof
unknown

For a single type, use the comma-ok assertion instead: n, ok := x.(int) — ok is false on a mismatch and it never panics.

Recap

ConceptIn Go
InterfaceA set of methods; any type with them satisfies it
SatisfactionImplicit, structural — no implements
anyinterface{}; holds anything, remembers its type
Safe recoveryComma-ok v, ok := x.(T)
Branch on typeType switch; first matching case wins

Next: 05 — Errors.