How can I use a Swift enum as a Dictionary key? (Conforming to Equatable)

Doug Knowles picture Doug Knowles · Jul 3, 2014 · Viewed 28.7k times · Source

I've defined an enum to represent a selection of a "station"; stations are defined by a unique positive integer, so I've created the following enum to allow negative values to represent special selections:

enum StationSelector : Printable {
    case Nearest
    case LastShown
    case List
    case Specific(Int)

    func toInt() -> Int {
        switch self {
        case .Nearest:
            return -1
        case .LastShown:
            return -2
        case .List:
            return -3
        case .Specific(let stationNum):
            return stationNum
        }
    }

    static func fromInt(value:Int) -> StationSelector? {
        if value > 0 {
            return StationSelector.Specific(value)
        }
        switch value {
        case -1:
            return StationSelector.Nearest
        case -2:
            return StationSelector.LastShown
        case -3:
            return StationSelector.List
        default:
            return nil
        }
    }

    var description: String {
    get {
        switch self {
        case .Nearest:
            return "Nearest Station"
        case .LastShown:
            return "Last Displayed Station"
        case .List:
            return "Station List"
        case .Specific(let stationNumber):
            return "Station #\(stationNumber)"
        }
    }
    }
}

I'd like to use these values as keys in a dictionary. Declaring a Dictionary yields the expected error that StationSelector doesn't conform to Hashable. Conforming to Hashable is easy with a simple hash function:

var hashValue: Int {
get {
    return self.toInt()
}
}

However, Hashable requires conformance to Equatable, and I can't seem to define the equals operator on my enum to satisfy the compiler.

func == (lhs: StationSelector, rhs: StationSelector) -> Bool {
    return lhs.toInt() == rhs.toInt()
}

The compiler complains that this is two declarations on a single line and wants to put a ; after func, which doesn't make sense, either.

Any thoughts?

Answer

Cezar picture Cezar · Jul 3, 2014

Info on Enumerations as dictionary keys:

From the Swift book:

Enumeration member values without associated values (as described in Enumerations) are also hashable by default.

However, your Enumeration does have a member value with an associated value, so Hashable conformance has to be added manually by you.

Solution

The problem with your implementation, is that operator declarations in Swift must be at a global scope.

Just move:

func == (lhs: StationSelector, rhs: StationSelector) -> Bool {
    return lhs.toInt() == rhs.toInt()
}

outside the enum definition and it will work.

Check the docs for more on that.