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

Reply via email to