tezvyn:

Swift Existential Types: The Polymorphism Box

AI-drafted, machine-checkedintermediate

An existential type is a boxed protocol: you see the interface, not the concrete type inside. Use it to store mixed concrete types behind one protocol, like [any Logger]. Prefer generics; existentials hide types and add dynamic dispatch overhead.

WHY IT EXISTS: Swift prefers compile-time type knowledge for performance and safety. However, real programs often handle values whose concrete types are unknown until runtime, such as decoding network payloads or storing mixed view models. Existential types let the compiler enforce a protocol contract while erasing the specific concrete type, enabling polymorphism without forcing every call site to know every implementation upfront.

THE MENTAL MODEL: Think of an existential as a shipping box with a capability label. The label says what the contents can do, but you cannot see inside. You can hand the box around and call its advertised methods, yet you lose the ability to know exactly what is inside. The box also takes more room than the raw item because it carries the label and padding.

HOW IT WORKS: When you assign a concrete value to an existential type, the Swift runtime wraps it in a container. This container holds the value, either inline or on the heap if large, plus witness tables for method dispatch and type metadata. Every call goes through dynamic dispatch rather than compile-time specialization. In modern Swift, you write this explicitly with the any keyword, making the boxing operation visible.

WHEN TO USE IT: Reach for an existential when you need heterogeneity at the storage level. Three signals are first, an array or dictionary holding multiple concrete types behind one protocol, second, a function returning different concrete types at runtime where opaque types fail, and third, type-erasing wrappers that hide complex associated types. The existential acts as a boundary so you stop propagating generic parameters through every layer.

WHEN NOT TO USE IT: Avoid existentials when a generic or some type would suffice. Generics let the compiler monomorphize code, eliminating dynamic dispatch and enabling stack allocation. Existentials cannot satisfy protocol conformance themselves, meaning any P does not conform to P, so you cannot pass one into a generic function expecting T: P without opening it. If your code handles one concrete type but merely hides the name, use an opaque some type instead.

ONE CANONICAL EXAMPLE: Imagine a telemetry system where each feature team defines its own analytics event struct conforming to an AnalyticsEvent protocol with a send method. The central manager cannot know every concrete event type at compile time because new features add events weekly. The manager stores an array of any AnalyticsEvent. When flushing, it iterates and calls send on each boxed event. The manager sees only the protocol interface, while the runtime dispatches each call to the correct concrete implementation. This keeps the manager decoupled from every feature module.

Get five bites like this every day.

Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.