In code written since Swift 5.6, you’ll see some or any before protocol names. some View, any Error, some Collection. Protocol names used to work directly in type positions, but the compiler now warns or errors unless you add any. What changed?
The short version: two things that already behaved differently had been collapsed into one notation, and Swift is bringing that distinction to the surface. This is the distinction between static and dynamic polymorphism introduced in the generics article. Part 3 of the intermediate series explains some and any.
Where the problem began — protocols have two faces in type positions
Protocols are originally a language of constraints. They express qualifications, such as a type conforming to Comparable. But once a protocol name appears as a variable or return type, its role changes.
let shapes: [Shape] = [Circle(), Square(), Triangle()]
This array contains different types. To make that possible, the compiler puts each value in a box: a box labeled “something conforming to Shape.” This is an existential type. The concrete type inside is known only at runtime, and method calls become indirect dispatch through the box to find the concrete implementation.
The problem is that this box is not free. Values of different sizes need a uniform box, so there is a separate existential container format, and large values are placed on the heap while the box stores only a pointer. Calls go through a witness table, producing dynamic dispatch, and much of the compiler’s optimization— inlining and specialization—becomes unavailable. There are functional limitations too. Since the concrete type inside the box has been erased, questions such as whether two Shapes have the same type become difficult to answer.
The old syntax hid this cost. Writing func draw(shape: Shape) creates a box, while writing func draw<S: Shape>(shape: S) uses generics without one. Two superficially similar pieces of code have completely different performance characteristics, but the syntax concealed the difference. That is exactly why Swift Evolution proposal SE-0335 introduced the any keyword: label the places where a box is created. It is another example of the “don’t hide costs” philosophy from Part 1 becoming a syntax change.
some — hiding without a box
If any is a box that can hold anything, some is the tool pointing in the opposite direction. some Shape means the concrete type is fixed to one type, but its name is not revealed. That is why it is called an opaque type.
func makeShape() -> some Shape {
Circle(radius: 10) // always Circle returns only one type
}
The caller does not know the name Circle, but the compiler does. So there is no box and no indirect call. Static dispatch and optimization remain fully available, so it is treated much like a generic in practice. The trade-off is flexibility: a function returning some Shape must return the same concrete type on every return path. Returning Circle or Square depending on a condition is a compile error.
SwiftUI’s var body: some View is the canonical use of this syntax. The type actually returned by body is a monster such as VStack<TupleView<(Text, Image)>>, and you neither can nor want to put that in the signature. some View solves the problem by hiding only the name while keeping the concrete type known to the compiler—without giving up performance. This is what that syntax, previously introduced as an example of “complexity beginners don’t need to know,” really is.
It is also useful to know about some in parameter positions. func draw(shape: some Shape) is shorthand for func draw<S: Shape>(shape: S) (SE-0341). It is a lightweight way to use generics when the type-parameter name is not needed in the body.
Choosing between them — some by default, any when justified
The difference between the keywords can be compressed into this table: some fixes one type at compile time (static dispatch, optimization possible, type relationships preserved), while any allows anything at runtime (dynamic dispatch, boxing cost, type erasure).
The practical rule is clear: use some (or generics) by default, and use any only when there is a real reason to mix multiple types.
There are roughly three justified uses for any. First, heterogeneous collections. Putting different types in one array, as in [any Shape], is impossible without a box. Second, stored properties whose type is decided at runtime, such as var strategy: any PaymentStrategy, where a different implementation is plugged in according to configuration. Protocol properties in the Strategy Pattern and dependency injection usually belong here. Third, functions whose return type varies by condition—the case some does not allow.
Put differently, if you only want a function parameter to accept any type conforming to this protocol, some is the answer. The type is fixed for each call. The Swift team’s guidance points in the same direction: promote to any only when values must be mixed or stored in a collection.
There is no need to exaggerate or dismiss the performance difference. In code handling a few UI events, the cost of any is irrelevant. But inside a loop running tens of thousands of times per second, boxing and blocked optimizations can make a measurable difference. As always, measure—and using some by default means there is less to measure in the first place.
Translating the error messages — what “add any” is telling you
Once you understand the distinction, compiler messages you previously memorized and ignored become readable.
“Use of protocol ‘X’ as a type must be written ‘any X’” means the syntax must acknowledge that using a protocol in a type position creates a box. Before mechanically adding any, ask whether this position really needs a box—or whether some or generics would work instead. That is the right way to consume this error.
“Protocol ‘X’ can only be used as a generic constraint” is the famous error from older Swift, shown when a protocol with an associatedtype or Self was used in a type position. It meant the compiler could not define the box format because it did not know the associated type. The language now permits much more through SE-0309 and primary associated types, but understanding this properly requires discussing associated types themselves. That is the next article’s topic.
Summary
- Using a protocol in a type position creates an existential type—a box—along with dynamic dispatch, container overhead, and type erasure. any is the honest label attached to that box.
- some does the opposite: one concrete type is fixed and only its name is hidden. There is no box, so performance is like generics; SwiftUI’s some View is the canonical example.
- Use some (or generics) by default, and reserve any for genuinely mixed types, such as heterogeneous collections, runtime-selected storage, and conditional returns.
- Read the compiler’s demand for any as a signal to recognize the boxing cost, and before adding it unconditionally, check whether some would work.
As previewed, the next article covers associatedtype: the roots of the “generic constraint” error, what it means for a protocol to contain a type placeholder, and primary associated types.

![Cover image for [Swift Intermediate #3] some vs any and the cost of existentials](/assets/images/posts/a880d464-6c55-4a06-bf69-b6f45434a102/1.jpg)