3

Wrote the following little test:

class Model { }
class SubModel : Model {}
class Collection<T: Model> {}

let collection = Collection<SubModel>()
var collections = [Collection]() // Also tried [Collection<Model>]() 
collections.append(collection)

The compiler fails on the append call. The message is: 'SubModel' is not identical to 'Model'. Based on my experience in all other languages I have used with generics, this should work. As a Collection of type SubMode should always be coercible to a Collection of type Model. Anyone run into this? Workarounds?

1 Answers1

6

This is happening because Swift does not support covariance in respect of generic type parameters.

For example:

class Animal {}
class Cat : Animal {}

Cat is clearly a subtype of Animal. However, this does not mean that, for instance, Array<Cat> is a subtype of Array<Animal>. In a language that supports covariance for generic type parameters, such as C#, Array<Cat> would be a subtype of Array<Animal>.

In your specific example, you can use a protocol and a non-generic Collection class to get around this limitation. Other situations might require more creativity.

protocol Collectible {}
class Model : Collectible {}
class SubModel : Model {}

class Collection {
    func append(element: Collectible) {
        // blah
    }
}

var collection = Collection()
collection.append(Model())
collection.append(SubModel())
var collections = [collection]

Actually, in your case, you could just make the element of Collection be a Model, i.e., func append(element: Model), but I did it this way to stress that protocols can sometimes be used to get around covariance limitations.

Gregory Higley
  • 15,923
  • 9
  • 67
  • 96