SwiftQA Random

How does Swift optimize enums with associated values?

The baseline: enums are tagged unions

enum Shape {
    case circle(radius: Double)
    case rectangle(width: Double, height: Double)
    case point
}

Naively: storage for the largest case’s payload plus a discriminator tag.

1. Tag-bit packing into spare bits

The compiler tries to steal unused bit patterns from the payload itself to encode the case discriminator (spare bit optimization) — often zero extra size.

2. The single-payload case — extreme version: Optional

For Optional<SomeClass>, the compiler exploits that a valid object pointer is never 0 — repurposing the all-zero bit pattern for .none. Result: Optional<SomeClass> is exactly the same size as a raw pointer — zero overhead.

3. Indirect enums — for recursive/self-referential cases

indirect enum Expression {
    case literal(Int)
    case add(Expression, Expression)
    case multiply(Expression, Expression)
}

indirect boxes that case’s payload onto the heap (a single pointer inline), making recursive value types representable. Per-case indirect case boxes only that case.

4. Layout is determined per concrete instantiation for generic enums

Optional<Int> and Optional<SomeHugeStruct> have completely different concrete layouts, via the value-witness table.

5. Copy/destroy cost follows payload, not the enum wrapper itself

The enum wrapper itself adds no additional ARC overhead beyond whatever its currently-active payload requires.

Summary table

OptimizationWhat it targetsEffect
Naive tagged unionBaselineSize = largest payload + tag
Spare bit packingPayloads with unused bit patternsTag folded into existing bits
Optional’s null-pointer optimizationClass referencesSame size as raw pointer
indirectRecursive payloadsHeap-boxes specific case(s)
Per-instantiation layout (generics)Generic enumsConcrete layout computed per T
Payload-driven copy/destroyARC / COW costZero overhead beyond active payload

One-liner if pressed: “Swift lays enums out as tagged unions sized to the largest case, but aggressively optimizes the tag away into spare bits of the payload when possible — the extreme case being Optional of a class reference, literally the same size as a raw pointer — and falls back to heap-boxing via indirect only when a case’s payload is recursively self-referential.”