(via ChatGPT)
- Existential type: Can hold a value of any type that conforms to the protocol.
- Requires heap allocation (boxing).
- Uses dynamic dispatch via a witness table.
- Used when you need flexibility (e.g. heterogeneous collections, dynamic polymorphism).
let x: any View = Text("Hello")
- Opaque return type: Compiler knows the exact concrete type, caller does not.
- Uses static dispatch — fast and inlineable.
- Only allowed in return position, not as parameter or variable types.
- Used for performance, especially in SwiftUI.
var body: some View {
Text("Hello")
}
An existential is a runtime value of an unknown concrete type that conforms to a protocol. You get an existential when using any Protocol
.
- Value is boxed in heap memory.
- Supports dynamic dispatch via witness table.
- Type-erased: concrete type information is not available at compile time.
Boxing wraps a value in a heap-allocated container so it can be treated as an existential.
- When using
any Protocol
(e.g.any Error
,any View
,any Hashable
) - When returning from a function or storing as a property
- Heap allocation (slower than stack)
- Reference counting
- Dynamic dispatch (no inlining)
- Pointer indirection
🚫 Swift does not allow the box to point to a value on the stack — it must be copied to the heap for safety.
Dispatch Type | Triggered By | Speed | Inlineable | Example |
---|---|---|---|---|
Static | Concrete types, some , generics |
✅ Fast | ✅ Yes | let x = Text("Hi") |
Dynamic | any , protocols, classes |
❌ Slower | ❌ No | let x: any View = Text("Hi") |
A witness table is a structure Swift generates to map protocol requirements to the concrete type's implementations.
It’s used for:
- Dynamic dispatch
- Calling protocol methods on an
any
existential - Ensuring the type actually conforms to the protocol
protocol Animal { func speak() }
struct Dog: Animal {
func speak() { print("Woof") }
}
let pet: any Animal = Dog()
pet.speak() // uses witness table for Dog:Animal
- Methods declared in the protocol = dynamically dispatched when using
any
. - Methods only in the extension = statically dispatched, even for
any
.
protocol Animal {
func greet()
}
extension Animal {
func greet() { print("Hello from protocol") }
func wag() { print("Default wag") }
}
struct Dog: Animal {
func greet() { print("Woof") }
func wag() { print("Custom wag") }
}
let dog: any Animal = Dog()
dog.greet() // Woof — dynamic
dog.wag() // Default wag — static! (extension-only method)
To make wag()
dynamically dispatched, you must declare it in the protocol.
Concept | Use any |
Use some |
---|---|---|
Flexibility | ✅ Can hold any conformer | ❌ Only one concrete type |
Performance | ❌ Heap allocation, slower | ✅ Fast, inlined |
Type Safety | ❌ Type-erased | ✅ Known to compiler |
Use In | ✅ Variables, parameters | ❌ Only return types |
Dispatch | ❌ Dynamic (witness table) | ✅ Static |