Opaque and Existential Types: some and any
Prerequisites: Protocol-oriented programming
When you use a protocol as a type, Swift asks you to choose between
two meanings with the keywords some and any.
any — "a box that can hold any conforming type"
any Shape is an existential type: a container that can hold a
value of any type conforming to Shape, and the specific type can
differ from value to value:
protocol Shape {
func area() -> Double
}
struct Square: Shape { let side: Double; func area() -> Double { side * side } }
struct Circle: Shape { let radius: Double; func area() -> Double { 3.0 * radius * radius } }
var shapes: [any Shape] = [Square(side: 2), Circle(radius: 1)]
shapes.append(Square(side: 3))
print(shapes.map { $0.area() })
[4.0, 3.0, 9.0]
Flexibility has a cost: the value is stored in a box, and each method call is looked up at runtime.
some — "one specific type, which I'm not naming"
some Shape is an opaque type: there is exactly one concrete
type behind it, known to the compiler, but hidden from the caller:
func makeTile() -> some Shape {
Square(side: 4)
}
func totalArea(of shape: some Shape) -> Double {
shape.area()
}
print(makeTile().area())
print(totalArea(of: Circle(radius: 2)))
16.0
12.0
- As a return type,
some Shapelets a function hide which shape it returns — but it must always return the same type. Returning aSquareon one path and aCircleon another is a compile error. - As a parameter type,
some Shapeis shorthand for a generic function:func totalArea<S: Shape>(of shape: S).
Because the compiler knows the real type, some has no boxing overhead
and keeps full type information.
You've already used some
Every SwiftUI view declares var body: some View. A view's body is a
deeply nested generic type that would be unreadable to write out —
some View says "a specific view type; let the compiler work it out".
Which to use
somewhen a single concrete type is involved — the default choice for parameters and return values.anywhen you genuinely need different types together — a heterogeneous array, or a stored property that can change to a different conforming type.
Common mistakes
- Returning different types from a
somefunction:function declares an opaque return type 'some Shape', but the return statements in its body do not have matching underlying types. Useany Shapeif that's really what you need.