しめ鯖日記

swift, iPhoneアプリ開発, ruby on rails等のTipsや入門記事書いてます

なぜIndexPathと配列リテラルは比較できるのか

こちらの記事を読んで、IndexPathをIntの配列リテラルから生成したりIndexPathとInt配列の比較ができる事を知りました。
今回はなぜこのような事ができるかを調べてみました。

[iOS][Swift] IndexPathはInt配列リテラルから作れる。Int配列リテラルとの比較もできる

let indexPath: IndexPath = [0, 0]

if indexPath == [1, 1] {
}

Intの配列リテラルからIndexPathを生成できる理由

Intの配列リテラルからIndexPathを生成できるのは、IndexPathがExpressibleByArrayLiteralプロトコルに準拠しているからでした。

let indexPath: IndexPath = [0, 0]

ExpressibleByArrayLiteralは下のようなもので、配列リテラルをそのクラスに変更する事ができます。

public protocol ExpressibleByArrayLiteral {

    /// The type of the elements of an array literal.
    associatedtype Element

    /// Creates an instance initialized with the given elements.
    public init(arrayLiteral elements: Self.Element...)
}

自作クラスをExpressibleByArrayLiteralに準拠させたらIntの配列リテラルからクラスに変換できるようになりました。

class MyClass: ExpressibleByArrayLiteral {
    typealias Element = Int
    
    required init(arrayLiteral elements: Int...) {
        print(elements) // → [1, 2, 3]
    }
}
let myValue: MyClass = [1, 2, 3]

IndexPathとInt配列の比較ができる理由

比較できるのは、IndexPathがEquatableに準拠しているからでした。
Equatableは下のようなプロトコルで、準拠するとそのクラス同士の比較ができます。

public protocol Equatable {

    /// Returns a Boolean value indicating whether two values are equal.
    ///
    /// Equality is the inverse of inequality. For any values `a` and `b`,
    /// `a == b` implies that `a != b` is `false`.
    ///
    /// - Parameters:
    ///   - lhs: A value to compare.
    ///   - rhs: Another value to compare.
    public static func ==(lhs: Self, rhs: Self) -> Bool
}

下のように比較演算子を使えるようになります。

class MyClass: Equatable {
    static func ==(lhs: MyClass, rhs: MyClass) -> Bool {
        return true
    }
}
print(MyClass() == MyClass())

これを先程のExpressibleByArrayLiteralと合わせることで、「IndexPath(row: 0, section: 0) == [0, 1]はIndexPath(row: 0, section: 0) == IndexPath(row: 1, section: 0)と同じ意味」 → 「EquatableによってIndexPath同士の比較は可能」→「IndexPathとIntの配列リテラルの比較ができる」となります。

試したところ、自作クラスをInt配列リテラルと比較する事ができました。

class MyClass: ExpressibleByArrayLiteral, Equatable {
    typealias Element = Int
    
    required init(arrayLiteral elements: Int...) {
        print(elements)
    }
    
    static func ==(lhs: MyClass, rhs: MyClass) -> Bool {
        return true
    }
}

let myValue: MyClass = [1, 2, 3]

print(myValue == [1, 2, 3])
switch myValue {
case [1, 1]: break
default: break
}