What are opaque return types (some) and why were they introduced?
The problem opaque types solve
Before Swift 5.1 (SE-0244), if you wanted a function to return “something conforming to protocol P” without committing to a concrete return type, your only option was returning the protocol type itself as an existential. This breaks down in two important cases:
1. Protocols with associated types (or Self requirements) can’t be used this way at all
protocol Container {
associatedtype Item
var items: [Item] { get }
}
func makeContainer() -> Container { ... } // ❌ compile error
2. Even when it compiles, returning the protocol type erases useful information — and costs performance
Every caller only knows “this is some Shape” — type identity is lost (can’t compare two Shape existentials even if both concrete types are the same and comparable), and existentials require witness tables and possibly heap-boxing.
The some solution: opaque types
func makeShape() -> some Shape {
return Circle()
}
- The compiler knows the concrete type at the definition site — compiles down the generics path, direct calls, no witness-table indirection.
- The caller does not know the concrete type, but relies on it consistently.
- Type identity is preserved across calls to the same function — two calls to
makeShape()always return the same underlying concrete type. - It unlocks associated-type protocols as return types —
some Containerworks even though-> Containerdoesn’t.
Concrete motivating example — SwiftUI
var body: some View {
VStack {
Text("Hello")
Image(systemName: "star")
}
}
View has an associated type (Body), so -> View isn’t even expressible as an existential in general.
Comparison table
-> Shape (existential) | -> some Shape (opaque) | |
|---|---|---|
| Concrete type known to compiler? | No (erased) | Yes, at definition site |
| Concrete type known to caller? | No | No |
| Same type guaranteed across calls? | No | Yes |
| Works with associated-type protocols? | Often no | Yes |
| Performance | Witness table, possible heap box | Same as concrete type |
Can compare with Equatable? | No (unless same concrete type provably) | Yes |
One-liner if pressed: “Opaque types (some) let a function hide its concrete return type from callers while still giving the compiler full knowledge of it internally — you get the abstraction of a protocol return type without the existential’s cost or its associated-type limitations, because the compiler treats it like specialized generic code rather than a boxed existential.”