Thanks for the code sample and link, but if I’m not wrong, this pattern doesn’t allow heterogeneous items.
If I have these definitions: struct Chicken {} struct Pig {} class ChickenFarm: Farm { func grow() -> Chicken { return Chicken() } } class PigFarm: Farm { func grow() -> Pig { return Pig() } } Then: var farms = // How do I define a set that can contain both ChickenFarm and PigFarm? farms.insert(AnyFarm<Chicken>(ChickenFarm())) farms.insert(AnyFarm<Pig>(PigFarm())) > On 17 Jul 2017, at 4:02 AM, Nevin Brackett-Rozinsky > <nevin.brackettrozin...@gmail.com> wrote: > > 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 <mailto: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 > > <mailto: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 <http://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 <http://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 <http://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 <http://lhs.name/> == rhs.name <http://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 <mailto:swift-users@swift.org> > >> https://lists.swift.org/mailman/listinfo/swift-users > >> <https://lists.swift.org/mailman/listinfo/swift-users> > > > > > > _______________________________________________ > swift-users mailing list > swift-users@swift.org <mailto:swift-users@swift.org> > https://lists.swift.org/mailman/listinfo/swift-users > <https://lists.swift.org/mailman/listinfo/swift-users> >
_______________________________________________ swift-users mailing list swift-users@swift.org https://lists.swift.org/mailman/listinfo/swift-users