Use of protocol 'Collection' as a type must be written 'any Collection'

Hi,
Why the compiler accepts the first insert but complains about the second insert2?
It make sense that compiler can't interfere the associated concert type of collection but why the first insert compiles.
I have looked briefly at any keyword proposal

class Trie <CollectionType: Collection> where CollectionType.Element : Hashable {
    init() {
    func insert(element:  CollectionType) {}
    func insert2(element:  Collection<Hashable>) {}
}

Thanks

It's a deliberately introduced inconsistency added when support for primary associated types was added. When using that form you must use the newer (and in Swift 6, only) existential form with any. This was done so that, when Swift 6 drops support for the older form, no code changes are needed for those particular uses.

3 Likes

In the first case, element is a specific type - whatever the generic parameter CollectionType is specified as, when you instantiate your Trie. e.g.:

let t = Trie<Array<Int>>()
t.insert(element: []) // CollectionType is 'Array<Int>', a concrete type.
            // The `[]` literal is inferred to be an empty Array of Ints.

In the second case you're using an existential; you're accepting any type that conforms to the Collection protocol with contents that are Hashable. e.g. it could be a Set<String>. Your implementation (and the compiler) will need to handle all possible conforming types, by determining the actual type at runtime.

For a while - prior to Swift 5.9 - existentials could be used implicitly, but it was decided that they should be clearly marked in the code, thus the new requirement to say any. Existentials have more overhead than concrete types, and I believe that is the main motivator for making their use more explicit.

2 Likes

More overhead, but also limitations on how they can be used, e.g. things like "why can't I use == to compare two Equatables?". Generic constraints are both more efficient and more likely to do what people want.

1 Like