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