AnyCollection with different generic types

AnyCollection([1, 2, 3]) // AnyCollection<Int>
AnyCollection(["a", "b", "c"]) // AnyCollection<String>

These two AnyCollection is great, but they have different Generic types, namely Int and String.

But I can still add these two AnyCollection to a single array, like this

// [AnyCollection<Any>]
let array = [AnyCollection([1, 2, 3]), AnyCollection(["a", "b", "c"])] 

I can't understand what happened to these two AnyCollection.

Why can it convert from String or Int to Any ??

I wrote some code to create one myself.

// ❌ Heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional
// Contains AnyMyCollection<String> && AnyMyCollection<Int> at a single collection.
var collections = [AnyMyCollection(Sports()), AnyMyCollection(Animals()), AnyMyCollection(Digits())]

protocol MyCollection<Element> {
    associatedtype Element
    func allValues() -> [Element]
}

// MARK: - AnyMyCollection

struct AnyMyCollection<Element> {
    internal var _box: _AnyMyCollectionBase<Element>
    
    init<C: MyCollection>(_ base: C) where C.Element == Element {
        self._box = _MyCollectionBox(base)
    }
}

extension AnyMyCollection: MyCollection {
    func allValues() -> [Element] {
        _box.allValues()
    }
}

final class _MyCollectionBox<Base: MyCollection>: _AnyMyCollectionBase<Base.Element> {
    init(_ base: Base) {
        _base = base
    }
    
    private var _base: Base
    override func allValues() -> [Base.Element] {
        _base.allValues()
    }
}

class _AnyMyCollectionBase<Element>: MyCollection {
    func allValues() -> [Element] {
        return []
    }
}

// MARK: - Diffrent Types of My Collection

struct Animals: MyCollection {
    typealias Element = String
    func allValues() -> [Element] {
        ["Monkey", "Tiger", "Lion"]
    }
}

struct Sports: MyCollection {
    typealias Element = String
    func allValues() -> [Element] {
        ["Basketball", "Football", "Baseball"]
    }
}

struct Digits: MyCollection {
    typealias Element = Int
    func allValues() -> [Element] {
        [1, 2, 3, 4, 5]
    }
}

I tried to follow the same technique but failed because the type of the element in AnyMyCollection is not the same.

The answer is simple.

First of all, we can look at the example for AnyCollection.

let a1 = AnyCollection([1, 2])
let a2 = AnyCollection(["1", "2"])
let a = [a1, a2] // ❌ Heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional
let b = [AnyCollection([1, 2]), AnyCollection(["1", "2"])] // ✅ Since every element is inferred as AnyCollection<Any> first

Then for "your" AnyMyCollection, it is the same.

I change your AnyMyCollection implementation for simplification.
But it's basically the same. If you still prefer to use your original implementation. You need to conform your collection's element conform to ExpressibleByArrayLiteral

var c1 = AnyMyCollection([1, 2])
var c2 = AnyMyCollection(["1", "2"])
var c = [c1, c2] // ❌ Heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional
var d = [AnyMyCollection([1,2]), AnyMyCollection(["1", "2"])] // ✅ Since every element is inferred as AnyMyCollection<Any> first
protocol MyCollection<MyElement> {
    associatedtype MyElement
    func allValues() -> [MyElement]
}

// MARK: - AnyMyCollection

struct AnyMyCollection<MyElement> {
    internal var _box: _AnyMyCollectionBase<MyElement>

    init<C: MyCollection>(_ base: C) where C.MyElement == MyElement {
        _box = _MyCollectionBox(base)
    }
}

extension AnyMyCollection: MyCollection {
    func allValues() -> [MyElement] {
        _box.allValues()
    }
}

final class _MyCollectionBox<Base: MyCollection>: _AnyMyCollectionBase<Base.MyElement> {
    init(_ base: Base) {
        _base = base
    }

    private var _base: Base
    override func allValues() -> [Base.MyElement] {
        _base.allValues()
    }
}

class _AnyMyCollectionBase<Element>: MyCollection {
    func allValues() -> [Element] {
        []
    }
}

extension Array: MyCollection {
    typealias MyElement = Element
    func allValues() -> [MyElement] {
        self
    }
}

There is no magic at Swift.Collection

3 Likes