I have a very simple playground:
protocol MyProtocol {}
struct MyType: MyProtocol {}
class MyClass <T: MyProtocol> {
func myFunction(array: [T]) {
if let myArray = array as? [MyType] {
println("Double!")
}
}
}
let instance = MyClass<MyType>()
let array = [MyType(), MyType()]
instance.myFunction(array)
Then it says "MyType is not a subtype of 'T'
" on the if let
line. Well, I think, however, MyType
and T
are compatible.
When I modified the if let
statement, it actually works:
if let first = array.first as? MyType
But now I can't cast array
to [MyType]
(Sure, I know it is Swift's static typing specification.)
I'm wondering what the problem is. My understanding about Generics? Or is it Swift language's limitation? If so, is there any way to do like this?
Thanks in advance.
Swift doesn’t have builtin behaviour to speculatively convert an array’s contents from one arbitrary type to another. It will only do this for two types that it knows have a subtype/supertype relationship:
class A { }
class B: A { }
let a: [A] = [B(),B()]
// this is allowed - B is a subtype of A
let b = a as? [B]
let a: [AnyObject] = [1,2,3]
// this is allowed - NSNumber is a subtype of AnyObject
let b = a as? [NSNumber]
struct S1 { }
struct S2 { }
let a = [S1(),S1()]
// no dice - S2 is not a subtype of S1
let b = a as? [S2]
The protocol doesn’t help either:
protocol P { }
struct S1: P { }
struct S2: P { }
let a = [S1(),S1()]
// still no good – just because S1 and S2 both conform to P
// doesn’t mean S2 is a subtype of S1
let b = a as? [S2]
Your example is basically a variant of this last one. You have an array of type [T]
, and you want to cast it to [MyType]
. It’s important to understand that you do not have an array of type [MyProtocol]
. Your generic type T
is a specific type, which has to implement MyProtocol
, but that’s not the same thing.
To see why you can’t just cast from any type to any other type, try this code:
protocol P { }
struct S: P { }
let a: [P] = [S(),S()]
let b = a as? [S]
This will generate a runtime error: "fatal error: can't unsafeBitCast between types of different sizes". This gives a hint as to why you can only cast an array from containing one reference type to a subtype – it’s because what is happening is just a bit cast from one pointer type to another. This will work for super/subtype classes, but not for arbitrary classes, structs, or protocols, as they have different binary representations.
Generic on a child-type is not a subtype of the same generic on a parent-type.
[MyProtocol]
in Swift actually translates to Array<MyProtocol>
(i.e. a generic). Same goes for [MyType]
being a shortcut for Array< MyType >
. This is why one does not directly cast to another.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With