Skip to content

Day 017 — Type Assertions & Type Switches

Month 1 · Week 3 · ⬅ Day 016 · Day 018 ➡ · Journal index

🎯 Learning Objective

Recover concrete types from interface values safely using type assertions (with the comma-ok form) and type switches, and know when not to.

📚 Topics

  • x.(T) single-value (panics) vs v, ok := x.(T) (comma-ok)
  • Asserting to a concrete type vs to another interface
  • switch v := x.(type) type switches · default case
  • Why over-using assertions is a design smell

📖 Reading / Sources

📝 Notes

  • A type assertion x.(T) extracts the concrete type/interface stored in interface value x. The single-result form s := x.(string) panics if the dynamic type isn't string → [[panic-recover]].
  • Always prefer the comma-ok form: s, ok := x.(string). When ok is false, s is the zero value of T and there's no panic. This mirrors map and channel-receive comma-ok → [[comma-ok-idiom]].
  • You can assert to an interface type, asking "does this value also satisfy this other interface?": if w, ok := r.(io.Writer); ok { ... }. Common for optional capabilities (e.g. interface{ Unwrap() error }).
  • A type switch handles many possible dynamic types cleanly: switch v := x.(type) { case int: ...; case string: ...; default: ... }. Inside each case, v already has that case's type. With multiple types in one case (case int, int64:), v keeps the interface type.
  • Type assertions are the mechanism behind errors.As and fmt's Stringer/Formatter checks — but in your own code, leaning on them is often a smell. Prefer adding a method to the interface over switching on concrete types → [[interfaces]].
  • A type switch's default catches anything unmatched; put it last. A nil interface matches case nil:.

💻 Code Examples

func describe(x any) string {
    switch v := x.(type) {
    case nil:
        return "nil"
    case int:
        return fmt.Sprintf("int: %d", v)
    case string:
        return fmt.Sprintf("string of len %d", len(v))
    case fmt.Stringer: // assert to ANOTHER interface
        return "stringer: " + v.String()
    default:
        return fmt.Sprintf("unknown %T", v)
    }
}

Full code: examples/month-01/interfaces/main.go · Run: go run ./examples/month-01/interfaces

🏋️ Exercises / Practice

Exercise Status Link
Describe(any) string covering int/string/Stringer/nil/default exercises/month-01/week-3/shapes
Comma-ok assertion to skip non-matching values exercises/month-01/week-3/shapes

🐛 Mistakes Made

  • Used n := x.(int) on a value that was a string → runtime panic interface conversion. Switched to comma-ok.
  • Put default before other cases and expected fallthrough; type-switch cases don't fall through and order of non-default cases doesn't matter, but default is the catch-all.

❓ Open Questions

  • When is a type switch legitimately the right tool vs a method on the interface? (Legit for errors.As-style unwrapping, formatting, and serializing heterogenous any data.)

🧠 Active Recall (answer without looking)

  1. Q: What's the difference between s := x.(string) and s, ok := x.(string)?

    A The single-value form panics if the dynamic type isn't `string`; the comma-ok form sets `ok=false` and `s` to the zero value instead of panicking.

  2. Q: In switch v := x.(type), what is the type of v inside case int, string:?

    A It keeps the original interface type (e.g. `any`), because the case matches more than one type. With a single-type case, `v` has that concrete type.

🪶 Feynman Reflection

An interface value hides its real type behind a contract. A type assertion is you peeking under the hood asking "is this really a string?" The safe way to peek is comma-ok, which answers yes/no instead of crashing. A type switch is just a tidy stack of those peeks for many possible types at once.

🕳️ Knowledge Gaps

  • Asserting to interfaces for "optional capabilities" — want more practice once I hit io interfaces.

✅ Summary

I can pull concrete types back out of interfaces safely with comma-ok assertions and type switches, and I know overusing them usually means I should have put a method on the interface instead.

⏭️ Next Steps / Prep for Tomorrow

  • Day 018: errors as ordinary values, and wrapping them with %w.

Time spent Difficulty Confidence
90 min 🟦🟦⬜⬜⬜ 🟦🟦🟦⬜⬜

Suggested commit: feat(examples): type assertions and type switches (day 017)