The standard pattern for type-erasure in Swift looks like this: protocol Farm { associatedtype Produce func grow() -> Produce }
private class _AnyFarmBase<T> : Farm { func grow() -> T { fatalError() } } private final class _AnyFarmBox<U: Farm>: _AnyFarmBase<U.Produce> { var farm: U init(_ x: U) { farm = x } override func grow() -> U.Produce { return farm.grow() } } public final class AnyFarm<V> : Farm { private let wrapped: _AnyFarmBase<V> func grow() -> V { return wrapped.grow() } init<W: Farm> (_ x: W) where W.Produce == V { wrapped = _AnyFarmBox(x) } } There is one little hiccough when you need an initializer in the abstract base class, which you can read about here <https://www.bignerdranch.com/blog/breaking-down-type-erasures-in-swift/> among other places. Hope that helps, Nevin On Sun, Jul 16, 2017 at 12:32 AM, Glen Huang via swift-users < swift-users@swift.org> wrote: > This sounds like the right approach! > > However, as I experimented with AnyHashable more, I found out that after > converting a concrete type to it, I could still convert back using “as”: > > AnyHashable(Foo()) as! Foo > > I guess that’s not the case with AnyNamed? I tried to imitate AnyHashable: > > struct AnyNamed: Named { > let base: Any > init<T: Named>(_ value: T) { > base = value > } > > var name: String { > // How do I convert `base` to `Named` here? > } > } > > But I have no idea what to put in `var name: String`. Also, even if we > managed to come up with a solution, would it magically allow direct casting > with “as”? Does the complier do something special for AnyHashable? > > > > On 16 Jul 2017, at 12:58 AM, Ole Begemann <o...@oleb.net> wrote: > > > > One way to do this in Swift is a method called type erasure. > > > > Type erasure means you create a new type that wraps any value whose > concrete type you want to erase. This new type also conforms to the > protocol. By convention the type is named Any... (compare AnyIterator and > AnySequence in the standard library, which do the same thing). > > > > struct AnyNamed: Named { > > private let _name: () -> String > > > > init<T: Named>(_ value: T) { > > _name = { value.name } > > } > > > > var name: String { > > return _name() > > } > > } > > > > AnyNamed is initialized with a generic value T: Named. Notice that the > initializer is generic, but the type itself isn't. Because AnyNamed can't > store value: T directly (then it would have to be generic over T), we > create a closure over value.name and store that instead. > > > > Now we can create a Set<AnyNamed> and, because AnyNamed conforms to > Named, treat the set's elements as values conforming to Named: > > > > var set = Set<AnyNamed>() > > set.insert(AnyNamed(Foo())) > > set.insert(AnyNamed(Bar())) > > > > for element in set { > > print(element.name) > > print(element.hashValue) > > } > > > > > > On 11.07.2017 12:10, Glen Huang via swift-users wrote: > >> Hi, > >> > >> I want to store some heterogeneous items all conform to a protocol > inside a set, is it something possible to do in swift? > >> > >> I tried this example: > >> > >> ``` > >> protocol Named: Hashable { > >> var name: String { get } > >> } > >> > >> extension Named { > >> var hashValue: Int { > >> return name.hashValue > >> } > >> > >> static func ==(lhs: Self, rhs: Self) -> Bool { > >> return lhs.name == rhs.name > >> } > >> } > >> > >> struct Foo: Named { > >> var name = "foo" > >> } > >> > >> struct Bar: Named { > >> var name = "bar" > >> } > >> > >> var item = Set<Named>() > >> item.insert(Foo()) > >> item.insert(Bar()) > >> ``` > >> > >> But it failed at `Set<Named>()` where it complained "Using 'Named' as a > concrete type conforming to protocol 'Hashable' is not supported”. > >> > >> After watching the WWDC session "Protocol-Oriented Programming in > Swift” by Dave Abrahams, I try to use protocols whenever possible. But I > can’t seem to overcome this barrier. Set.Element must confirm to Hashable, > which inherits from Equatable, which has self requirement, which ultimately > means that Set.Element all must be of the same type. So it seems it’s > impossible to have heterogeneous items using protocol. Is that the case? > >> > >> My use case is this: > >> > >> I have an object that can contain two sets of other objects: > >> > >> ``` > >> class Parent { > >> var foos: Set<Foo> > >> var bars: Set<Bar> > >> } > >> ``` > >> > >> I want to define a computed property “all” that is the union of the two > sets. Foo and Bar conform to the same protocol. I wonder what return type I > should use for the union? Do I have to go back to OOP and define a super > class for Foo and Bar? > >> > >> Thanks. > >> _______________________________________________ > >> swift-users mailing list > >> swift-users@swift.org > >> https://lists.swift.org/mailman/listinfo/swift-users > > > > > > _______________________________________________ > swift-users mailing list > swift-users@swift.org > https://lists.swift.org/mailman/listinfo/swift-users >
_______________________________________________ swift-users mailing list swift-users@swift.org https://lists.swift.org/mailman/listinfo/swift-users