0

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.

Ky -
  • 30,724
  • 51
  • 192
  • 308
  • One approach would be to simply create a wrapper type, such as in Rob's answer to [this question](http://stackoverflow.com/q/39499137/2976878). Another option would be to create a 'dummy' protocol, such as shown in [this Q&A](http://stackoverflow.com/q/37977817/2976878) – Hamish Nov 24 '16 at 10:13

1 Answers1

1

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)")
Vasily Bodnarchuk
  • 24,482
  • 9
  • 132
  • 127
  • Sorry, I should have been clear. I _need_ `Foo` and `Baz` to be structs. They represent structs that exist in external modules which I cannot change. The dictionary also comes from this module. – Ky - Nov 23 '16 at 17:34
  • That certainly compiles and runs properly, yes. However, it won't be nice having that var popping up in autocomplete for every single dictionary. I'll consider this answer, but I probably won't use it in practice. – Ky - Nov 23 '16 at 20:47
  • 1
    There are restrictions Swift language. I left the two examples in its reply. Either you use classes and then hasSpecialPredefined property will be available only dictionary [Foo: Baz], or you can use structs and hasSpetsialPredefined will be available to all dictionaries. – Vasily Bodnarchuk Nov 23 '16 at 21:05