5

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.

2 Answers 2

4

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.

Sign up to request clarification or add additional context in comments.

4 Comments

Once again an excellent explanation. Though it makes me feel that generic types do not seem to be a good language concept since using them simply needs an egg head.
Thanks a lot for your quick and detail answer. It is enough for me to convince that I can’t do it in this way, but now… how can I do this? Exactly what I want (and actually the "Double!" in the original question is the trace of it,) is a class with generics, and its generic type is either Double or Int. I want know what type passed and cast it into the proper array type. I tried to do this by the way like above (and it turned out to be impossible as you teach me.) Do you have any ideas? Pardon me for my greediness.
Perhaps I solved it myself by implementing some functions for the protocol. Thanks.
@Akkyie sounds like you might have a use-case for an enum with associated values
3

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.