In Swift, I notice that I can upcast an object that conforms to a protocol called, let's say SubProtocol
to another protocol called SuperProtocol
which is a super protocol of SubProtocol
. But I can't do the same with an array of the protocol. Here's the example code that I ran in Playground:
protocol SuperProtocol {
}
protocol SubProtocol: SuperProtocol {
}
class MyObject: SubProtocol {
}
let value1: SubProtocol = MyObject()
let value2: SuperProtocol = value1 // No error here. Upcasting works.
let array1: [SubProtocol] = [MyObject()]
let array2: [SuperProtocol] = array1 // Error here "Cannot convert value of type '[SubProtocol]' to specified type '[SuperProtocol]'"
This seems counter-intuitive, and I'm wondering why it's not allowed.
Try this code - just checked, works fine
The reason has to do with how protocols inherit differently from classes.
Consider first that protocols can have default implementations, for example:
Let's make classes that conform to these protocols:
Their
legs()
methods respond as we'd expect:But now let's cast
cow
toMammal
:cow
had 4 legs, but now it has2
. This is because, with protocols, the currently known type determines which default implementation is used. So casting the array doesn't work — I think the reasoning is that it would be unexpected for an array cast to alter its contained objects' behavior.Workaround
As you've noted, this won't work:
If you want, you can work around this limitation by mapping the array to the protocol you want:
More information on how protocols inherit is available in the Protocol-Oriented Programming in Swift session from this year's WWDC - transcript here.