Associated valueのあるenumは自動でEquatableにならないという話

事象と原因

以下のコードはコンパイルに失敗する。

enum Enum {
    case Value
    case WithNumber(Int)
}

let b = Enum.Value == Enum.Value // エラー: "Cannot invoke '==' with an argument list of type '(Enum, Enum)'"

お前はなにを言っているんだ、と思ったが、どうやらこれは正しい挙動のようだ。

公式Blogによれば、

Simple enums that have no associated data (like MyBool) are automatically made Equatable by the compiler, so no additional code is required.

のようである。なるほど、case WithNumber(Int)の行をコメントアウトするとコンパイルに成功する。

対応

当然、自分でEnumをEquatableにして、==

func ==(lhs: Enum, rhs: Enum) -> Bool {
    switch (lhs, rhs) {
    case (.Value, .Value): return true
    case (.WithNumber(let left), .WithNumber(let right)): return left == right
    default: return false
    }
}

と定義すればよい。switchをネストしなくてよいのはタプルとパターンマッチの強みであろう。where left == right: return trueとすることもできる。

とはいえ、このケースはAssociated valueがIntで自明にEquatableなのだから、察して大目にみてほしい、とは思う。

関連: Hashableについて

The Swift Programming Languageによれば、Associated valueつきenumはHashableでもなくなるようだ。

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

Associated valueつきenumは普通のenumとは似て非なるものと考えたほうがよいのかもしれない。

参考文献