Article

What is Type Erasure?

Tyler Johnson

February 3, 2020

Protocols in Swift are an odd beast. This is due to the fact that there are two types of protocols — protocols which leverage generics under-the-hood and protocols which do not. The latter are the protocols we know and love from Objective-C while the former are protocols with associated types or methods/properties which reference Self:

When we define protocols in our code, the compiler creates a protocol witness which allows the compiler to perform type-checking. In the case of protocols with associated types, the protocol witness requires the use of generics. It’s this use of generics which prevents us from creating an array of a protocol with associated types or using it as a parameter to a method.

The Type Erasure Pattern

Type erasure is a pattern applied to protocols with associated types or self requirements. It allows us to work around the strong type safety of the Swift compiler in order to use these protocols in a way that is similar to using protocols without associated types.

To apply type erasure:

  1. Use generics to abstract away the types in the protocol.
  2. Translate properties from the protocol directly to new properties on the erased type.
  3. Translate method signatures from the protocol into closure properties on the erased type. Properties on the erased type maintain a reference to the concrete implementation from types that conform to the protocol.
  4. Add an initializer which accepts an instance of the protocol. Notice how we add the protocol as a generic constraint on the initializer. In the initializer, assign each property/method to properties on the erased type.

Now, instead of using our protocol, we use the erased type as a method parameter or when creating an array. As an example, we can create a concrete type that conforms to a self-referential protocol:

We can wrap SelfReferentialWithString in our type-erased type, create an array, and perform the same operations on the erased type that we would expect to be able to perform using protocols without associated types or self requirements:

Conclusion

Type erasure is a pretty straight-forward concept once you understand the use-case and how it’s applied. Now that you’ve seen the pattern, it will be easier to identify in the wild and, hopefully, to reason about.



Notes

What is a protocol witness?

A protocol witness is a way for the compiler to make our protocol into a concrete type. This type is only known to the compiler and cannot be used in our code — unless of course we decide to write the code ourselves! For the protocols above, the compiler generates concrete types that look like this:

A protocol witness is simply an instance of one of these types. There’s no problem when creating an array of our protocol witnesses, but notice how we have to specify a type for the generic parameter.

When using protocols, we’re not able to provide this necessary type information to the compiler. This is why the compiler complains that our protocol can only be used as a generic constraint when it has associated type or self requirements.