Search code examples
swiftgenericsstructswift-extensions

Can one extend a Swift Dictionary by a struct?


I've tried the solution in extension of Dictionary where <String, AnyObject> but it won't compile for me.

I simply want to constrain a dictionary extension to struct types. Is there any way of accomplishing this?

import Cocoa

struct Foo: Hashable {
    let bar: String

    static let predefinedFoo = Foo(bar: "something")

    var hashValue: Int { return bar.hashValue }

    public static func ==(lhs: Foo, rhs: Foo) -> Bool {
        return lhs.hashValue == rhs.hashValue
    }
}


struct Baz {
    let isSpecial: Bool
}


extension Dictionary where Key: Foo, Value: Baz { // Note that the == syntax does not compile, either
    var hasSpecialPredefined: Bool {
        return self[.predefinedFoo]?.isSpecial ?? false
    }
}

let test: [Foo: Baz] = [.predefinedFoo: Baz(isSpecial: true)]

test.hasSpecialPredefined

With the above code, I get two compile errors:

error: type 'Key' constrained to non-protocol type 'Foo'
error: type 'Value' constrained to non-protocol type 'Baz'
error: '[Foo : Baz]' is not convertible to '<<error type>>'
    test.hasSpecialPredefined
    ^~~~

Is it possible to constrain an extension by a struct? If not, why not? This seems perfectly reasonable.


Note that Foo and Bar, here, are not under my control. They represent structs that are defined in an external module, and the dictionary I want to extend also comes from this module. Answers should assume Foo will always be a struct, and that struct will always be the key type for the dictionary.


Solution

  • Try my version

    1. Code (with struct)

    import Foundation
    
    struct Foo: Hashable {
        let bar: String
    
        static let predefinedFoo = Foo(bar: "something")
    
        var hashValue: Int { return bar.hashValue }
    
        static func ==(lhs: Foo, rhs: Foo) -> Bool {
            return lhs.hashValue == rhs.hashValue
        }
    }
    
    struct Baz {
        let isSpecial: Bool
        init(isSpecial: Bool) {
            self.isSpecial = isSpecial
        }
    }
    
    extension Dictionary where Key: Any, Value: Any {
        var hasSpecialPredefined: Bool {
            for key in keys {
                if let _key = key as? Foo, _key == .predefinedFoo, let value = self[key] as? Baz {
                    return value.isSpecial
                }
            }
            return false
        }
    }
    
    let foo1 = Foo(bar: "ddddd")
    var test: [Foo: Baz] = [foo1: Baz(isSpecial: true)]
    print("\(test), hasSpecialPredefined: \(test.hasSpecialPredefined)")
    test[.predefinedFoo] =  Baz(isSpecial: true)
    print("\(test), hasSpecialPredefined: \(test.hasSpecialPredefined)")
    

    Result

    enter image description here

    2. Code (with classes)

    import Foundation
    
    class Foo: Hashable {
        let bar: String
    
        init(bar:String) {
            self.bar = bar
        }
    
        static let predefinedFoo = Foo(bar: "something")
    
        var hashValue: Int { return bar.hashValue }
    
        public static func ==(lhs: Foo, rhs: Foo) -> Bool {
            return lhs.hashValue == rhs.hashValue
        }
    }
    
    class Baz: AnyObject {
        let isSpecial: Bool
        init(isSpecial: Bool) {
            self.isSpecial = isSpecial
        }
    }
    
    extension Dictionary where Key: Foo, Value: Baz {
        var hasSpecialPredefined: Bool {
            for key in keys {
                if key == .predefinedFoo {
                    return self[key]?.isSpecial ?? false
                }
            }
            return false
        }
    }
    
    let foo1 = Foo(bar: "ddddd")
    var test: [Foo: Baz] = [foo1: Baz(isSpecial: true)]
    print ("hasSpecialPredefined: \(test.hasSpecialPredefined)")
    test[.predefinedFoo] =  Baz(isSpecial: true)
    print ("hasSpecialPredefined: \(test.hasSpecialPredefined)")