protocolでEquatableを継承する方法


以下のようにEquatableを継承したプロトコルを扱いたいですがコンパイルエラーになってしまいます。  
どうすればいいでしょうか

[code]

    protocol Hoge: Equatable {
        var name: String { get }
    }
    
    class HogeA: Hoge {
        static func ==(lhs: HogeA, rhs: HogeA) -> Bool {
            return lhs.name == rhs.name
        }
    
        let name = UUID().uuidString
    }
    
    class HogeB: Hoge {
        static func ==(lhs: HogeB, rhs: HogeB) -> Bool {
            return lhs.name == rhs.name
        }
    
        let name = UUID().uuidString
    }
    
    let hogeA1 = HogeA()
    let array: [Hoge] = [HogeA(), HogeB(), hogeA1] // コンパイルエラー Protocol 'Hoge' can only be used as a generic constraint because it has Self or associ
    array.index(of: hogeA1) // 2が返ってきてほしい
    
[/code]




エラーメッセージにある通り、Swiftでは`Self`や関連型を持つprotocolを普通の型のように変数のデータ型や配列の要素型としては使うことはできません。

`Equatable`プロトコルは、

[code]

    public protocol Equatable {
        public static func ==(lhs: Self, rhs: Self) -> Bool
    }
    
[/code]

のように定義されており、もろに`Self`が使われていますから、`Equatable`や`Equatable`を継承したprotocolを普通の型のように使うことはできません。

そもそもなぜそのようことが禁止されているかを考えるには、あなたのコードは丁度良いでしょう。

[code]

    let array: [Hoge] = [HogeA(), HogeB(), hogeA1]
    
[/code]

このようなコードが許されたとすると、次の行は処理できなくなってしまいます。

[code]

    array.index(of: hogeA1)
    
[/code]

`HogeA`に定義された`==`は`HogeA`と`HogeA`を比較するもの、`HogeB`に定義された`==`は`HogeB`と`HogeB`を比較するものです。したがって`hogeA1`と`HogeB()`は比較できないと言うことになります。

* * *

あなたの例ならば、`Hoge`から`Equatable`を外してしまうと言うのが一つの解決方法でしょう。

[code]

    protocol Hoge {
        var name: String { get }
    }
    
    //`HogeA`, `HogeB`クラスの定義は同じで良い
    
    let hogeA1 = HogeA()
    let array: [Hoge] = [HogeA(), HogeB(), hogeA1]
    if let index = array.index(where: {$0.name == hogeA1.name}) {
        print(index) //->2
    } else {
        print("Not Found")
    }
    
[/code]




エラーメッセージにある通り、Swiftでは`Self`や関連型を持つprotocolを普通の型のように変数のデータ型や配列の要素型としては使うことはできません。

`Equatable`プロトコルは、

[code]

    public protocol Equatable {
        public static func ==(lhs: Self, rhs: Self) -> Bool
    }
    
[/code]

のように定義されており、もろに`Self`が使われていますから、`Equatable`や`Equatable`を継承したprotocolを普通の型のように使うことはできません。

そもそもなぜそのようことが禁止されているかを考えるには、あなたのコードは丁度良いでしょう。

[code]

    let array: [Hoge] = [HogeA(), HogeB(), hogeA1]
    
[/code]

このようなコードが許されたとすると、次の行は処理できなくなってしまいます。

[code]

    array.index(of: hogeA1)
    
[/code]

`HogeA`に定義された`==`は`HogeA`と`HogeA`を比較するもの、`HogeB`に定義された`==`は`HogeB`と`HogeB`を比較するものです。したがって`hogeA1`と`HogeB()`は比較できないと言うことになります。

* * *

あなたの例ならば、`Hoge`から`Equatable`を外してしまうと言うのが一つの解決方法でしょう。

[code]

    protocol Hoge {
        var name: String { get }
    }
    
    //`HogeA`, `HogeB`クラスの定義は同じで良い
    
    let hogeA1 = HogeA()
    let array: [Hoge] = [HogeA(), HogeB(), hogeA1]
    if let index = array.index(where: {$0.name == hogeA1.name}) {
        print(index) //->2
    } else {
        print("Not Found")
    }
    
[/code]

