[swift-users] ⁨Is it possible to store a set of heterogeneous items with protocol?

Ole Begemann ole at oleb.net
Sat Jul 15 11:58:03 CDT 2017


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 at swift.org
> https://lists.swift.org/mailman/listinfo/swift-users




More information about the swift-users mailing list