What is an existential container? What is a witness table?

1.4.6 · L5 · Learning

Predict first

What does this print on a 64-bit Mac?

print(MemoryLayout<any CustomStringConvertible>.size)
  • 8
  • 16
  • 40

Mental model. An existential is a shipping crate: the contents, a manual for how to move them (value witness table), and a phone book of who can do what (protocol witness table).

An existential (any P) is stored in a container with three parts:

  • Value buffer — three words inline; larger values are boxed on the heap.
  • Value witness table — how to copy, move, destroy, and size the stored value.
  • Protocol witness table(s) — one function pointer per protocol requirement, pointing at the concrete implementation.

Calling a protocol method on any P is a dynamic dispatch through the witness table. That is why any is more expensive than a generic some P / <T: P>, which the compiler can specialize and inline.

Three words of inline buffer, one word of type metadata, and one word per protocol witness table: 5 × 8 = 40 bytes.

Common traps

  • “A witness table is a vtable.” Similar idea, different mechanism: vtables are per-class and support inheritance; witness tables are per type + protocol conformance.
  • “Existentials always allocate.” No — up to three words fit inline. Only larger payloads get boxed.

They might follow up with

  • What is a value witness table used for? Copying and destroying the erased value when the compiler does not know its concrete type.
  • Why is a protocol extension method not in the witness table? Because it is not a requirement — it is a statically dispatched free function.

Related questions

  • 1.4.2 some View vs any View — what is the difference, and why does SwiftUI use some?
  • 1.3.3 Protocol extension vs inheritance for default behaviour — what are the tradeoffs?
  • 1.4.3 What is the cost of any vs some on a hot path?
  • 1.11.3 You added a method in a protocol extension and overrode it on a concrete type, but calling through any P does…

← What is the difference between some in return position and some in…   What is generic specialization? When does it not happen? →