Type erasure in swift
When defining a protocol, it’s sometimes useful to declare one or more associated types as part of the protocol’s definition. An associated type gives a placeholder name to a type that is used as part of the protocol. The actual type to use for that associated type isn’t specified until the protocol is adopted. Associated types are specified with the associatedtype keyword. from Swift docs
Another words, Protocol with associated type is a way of declaring Generic protocol.
Problem
Protocol CustomProtocol
has associated type AssociatedType
. It guarantees that if an object adopts CustomProtocol
argument
parameter of foo
function and return type of bar
function are of the same type AssociatedType
.
protocol CustomProtocol {
associatedtype AssociatedType
func foo(argument: AssociatedType)
func bar() -> AssociatedType
}
We want to create an Array
of objects that are adopting CustomProtocol
let array = [CustomProtocol]()
It won’t work. Compiler will show the following Error:
Protocol 'CustomProtocol' can only be used as a generic constraint because it has Self or associated type requirements
This erorr appears because the type of the object contained in the array
is only “partially defined” until the protocol is adopted, which means all associatedtype
s are defined.
Type erasure in Swift
Type erasure can be explained as the process of enforcing type constraints only at compile time and discarding the element type information at runtime.
The example of Type erasure is AnySequence
. If you type AnySequence
in the Playground, and then jump to its definition, you will see the following in the top left corner:
Adding one more level of abstraction (AnySequence<Element>
) fixes the problem. Operations are now forwarded to the underlying sequence, but the key difference is that Compiler now knows what the associatedtype
is. In case of Sequence
it is Element
(Actually, there is more associated types. See my article about Sequences).
public struct AnySequence<Element> {
// Implementation here
}
extension AnySequence : Sequence {
/// A type that provides the sequence's iteration interface and
/// encapsulates its iteration state.
public typealias Iterator = AnyIterator<Element>
/// Creates a new sequence that wraps and forwards operations to `base`.
public init<S>(_ base: S) where Element == S.Element, S : Sequence
}
Solution
Now it’s pretty easy.
public struct AnyCustomProtocol<T>: CustomProtocol {
func bar() -> T {
fatalError("Needs implementation")
}
func foo(argument: T) {
}
}
let array = [AnyCustomProtocol<Any>]() // works fine
Real world usage
For real-world usage example please, see my answer on stackoverflow.com.
Playground code
See Github gist