Background: Type erasure is a pattern that emerged in Swift as a response to the limitations of protocols with associated types or Self requirements. A protocol cannot be used as a type directly without type erasure if it contains associated types, because the compiler does not know the specific implementation. This is often encountered when creating data containers, collections, streams, where abstraction is important.
Problem: If there is a protocol with an associated type, for example:
protocol Animal { associatedtype Food func eat(_ food: Food) }
you cannot declare a variable of type Animal. We need a "type-erasing wrapper" that allows accessing different concrete types through abstraction:
let zoo: [any Animal] // Compilation error
Solution: Type erasure is implemented using wrappers (for example, Box pattern or struct AnyXxx), providing a uniform interface while hiding the details of the actual type:
struct AnyAnimal<F>: Animal { private let _eat: (F) -> Void init<A: Animal>(_ base: A) where A.Food == F { _eat = base.eat } func eat(_ food: F) { _eat(food) } }
Now we can store different implementations of Animal with the same Food:
let animals: [AnyAnimal<Grass>] = [AnyAnimal(Cow()), AnyAnimal(Sheep())]
Key features:
Can you use a protocol with an associated type without type erasure as a property type or array?
No, you cannot. The compiler requires specification of all associated types. For example, the following declaration will cause an error:
let array: [Animal] // Error: 'Animal' can only be used as a generic constraint
Can type erasure replace inheritance?
No, type erasure is not a complete replacement for inheritance. It addresses the abstraction problem for protocols with associated types, while inheritance is used to implement common logic and code reuse among classes.
Do all methods and properties of a protocol need to be implemented inside the type erasure wrapper?
Yes, they must. Type erasure works only if the wrapper fully replicates the external interface of the protocol; otherwise, some functionality will be unavailable.
In a production project, all work with data sources is built around type erasure, even when there is no need for associated types. The code becomes hard to maintain, and new developers get confused.
Pros:
Cons:
Type erasure is applied only for abstraction of the data source, which encapsulates different loading strategies (network, local), each with its own types. Otherwise, the code is transparent.
Pros:
Cons: