TheSnowFox
TheSnowFox

Reputation: 3

Swift generic collection of Element cannot convert to collection of Any

I'm facing a problem I don't understand. In my project, I want to make a collection of elements with some customised methods (like an update from a server). But when I try to group all these collections in an array, I get an error: "Cannot convert value of type MyCollection<someElement> to expected argument type MyCollection<Any>"

What I don't understand is that the same code with Array is working... Array isn't a collection?

// My collection which would contain an update method
class MyCollection<Element> {
    var object:Element? = nil
}

let x = MyCollection<Int>()
var list = [MyCollection<Any>]()
list.append(x) //Cannot convert value of type 'MyCollection<In>' to expected argument type 'MyCollection<Any>'

let a = Array<Int>()
var lista = [Array<Any>]()
lista.append(a) //Doesn't get error at all...

I know I can do this with an array of the specific type but by grouping all of MyCollection in an array, I wish to use a code like :

func update() {
    for e in list { // array of MyCollection<Any>
        e.update()
    }
}

Thank you in advance for your help ;)

Upvotes: 0

Views: 738

Answers (1)

Sweeper
Sweeper

Reputation: 271625

Being able to convert from SomeType<Subtype> to SomeType<Supertype> is called covariance. In Swift, Array<T> is covariant on T by "compiler magic", and you can't do the same for your own types.

The type checker hardcodes conversions from Array to Array if there is a conversion from T to U. Similar rules exist for Optional and Dictionary. There's no mechanism for doing this with your own types.

Your own generic types are always invariant, meaning that there is never a conversion between SomeType<T> to SomeType<U>, as long as T and U are different types.

Let's imagine what would happen if the conversion on MyCollection were allowed. You could do:

let myCollectionInt = MyCollection<Int>()
let myCollectionAny: MyCollection<Any> = myCollectionInt // suppose you can do this
myCollectionAny.object = "string" // myCollectionAny.object is of type Any?, so this should be ok

We've set myCollectionAny.object to "string", but MyCollection is a reference type, so myCollectionInt.object should also be "string". But myCollectionInt.object is an Int?!

Of course this type-unsafety is also a problem with arrays, but the language designers have decided that casting arrays is a common enough thing to do, that disallowing it would do more hard than good.

Upvotes: 1

Related Questions