I am struggling with building a type eraser for a Collection wrapper. The protocols are simplified and are defined by a package, so I can't change them. Also I can't use any new iOS13 features like opaque result types.
I have created quite a few type erasers before and never really had any issues, but this time I am stuck. The problem is the Self requirement in the base protocol.
Here is the code I have so far, which does everything except I can't see how I can forward the calls to isEqual
to my box, as the base class is not adopting the erased protocol, but rather the subclass. (Precedent in the standard lib is _AnyIteratorBox). If the abstract base class would adopt the protocol, I'd have a problem implementing the initializer requirement in the subclass .
Do I need another type-eraser? I have a gut feeling somehow…
Any help would be greatly appreciated.
import Foundation
// These protocols are a given and not up for change
protocol Diffable {
func isEqual(to: Self) -> Bool
}
protocol Container: Diffable {
associatedtype Collection: Swift.Collection where Collection.Element: Diffable
init<C: Swift.Collection>(source: Self, elements: C) where C.Element == Collection.Element
var elements: Collection { get }
}
// A type eraser for containers of the same element type
class AnyContainer<Element: Diffable>: Container {
var _box: _AnyContainerBoxBase<Element>
init<F>(_ container: F) where F: Container, F.Collection.Element == Element {
_box = _AnyContainerBox(container)
}
required init<C>(source: AnyContainer<Element>, elements: C) where C: Swift.Collection, C.Element == Element {
self._box = _AnyContainerBox(source: _AnyContainerBox(source), elements: elements)
}
var elements: [Element] { _box.elements }
func isEqual(to: AnyContainer<Element>) -> Bool {
fatalError("What now…?") // <- how is this possible to implement?
}
}
// Conforming this to `Foo` instead of the base class because if the initializer requirement
class _AnyContainerBox<F: Container>: _AnyContainerBoxBase<F.Collection.Element>, Container {
var container: F
init(_ container: F) {
self.container = container
}
required init<C>(source: _AnyContainerBox<F>, elements: C) where C: Swift.Collection, C.Element == F.Collection.Element {
self.container = F.init(source: source.container, elements: elements)
}
override var elements: [F.Collection.Element] {
Array(container.elements)
}
// This doesn't override, so it doesn't work as intended :(
func isEqual(to: _AnyContainerBox<F>) -> Bool {
container.isEqual(to: to.container)
}
}
class _AnyContainerBoxBase<Element> {
var elements: [Element] { fatalError("Abstract") }
// Sadly, this is what would get called always
func isEqual(to: _AnyContainerBoxBase<Element>) -> Bool { fatalError("Abstract") }
}