Невозможно использовать индексы .contains() в расширении коллекции в Swift 3

Я написал следующее расширение в Swift 2.3:

extension CollectionType {
    /// Returns the element at the specified index iff it is within bounds, otherwise nil.
    subscript (safe index: Index) -> Generator.Element? {
        return indices.contains(index) ? self[index] : nil
    }
}

Однако, оказывается, что Swift 3.0 не имеет функции contains(). Вместо этого он предлагает мне следующий синтаксис для этого метода:

indices.contains(where: { (<#Self.Indices.Iterator.Element#>) -> Bool in
    <# code ??? what should it do??? #>
})

Проблема в том, что я не знаю, что она должна содержать внутри блока. Любая помощь по его миграции, пожалуйста?

Ответ 1

Обновление Swift 4

В Swift 4 благодаря возможность иметь предложения where для связанных типов, Collection теперь предусматривает, что Indices Element - это тот же тип, что и Collection Index.

Это означает, что мы можем просто сказать:

extension Collection {

    /// Returns the element at the specified index iff it is within bounds, otherwise nil.
    subscript (safe index: Index) -> Element? {
        return indices.contains(index) ? self[index] : nil
    }
}

Swift 3

Протокол Sequence в Swift 3 по-прежнему имеет метод contains(_:), который принимает элемент последовательности, если последовательность имеет Equatable элементов:

extension Sequence where Iterator.Element : Equatable {
    // ...
    public func contains(_ element: Self.Iterator.Element) -> Bool
    // ...
}

Проблема, с которой вы сталкиваетесь, связана с изменением типа требования к свойствам Collection Indices. В Swift 2 он был типа Range<Self.Index>, однако в Swift 3 он имеет тип Indices (связанный тип Collection):

/// A type that can represent the indices that are valid for subscripting the
/// collection, in ascending order.
associatedtype Indices : IndexableBase, Sequence = DefaultIndices<Self>

Поскольку в Swift для протокола Collection в настоящее время нет способа выразить, что Indices Iterator.Element имеет тип Index (это будет возможно в будущей версии Swift), компилятор не знает, что вы можете передать что-то типа Index в contains(_:). Это связано с тем, что в настоящее время вполне возможно, чтобы тип соответствовал Collection и реализовал Indices с любым типом элемента, который он хочет.

Поэтому решение состоит в том, чтобы просто ограничить расширение, чтобы Indices имел элементы типа Index, позволяя вам передать Index в contains(_:):

extension Collection where Indices.Iterator.Element == Index {

    /// Returns the element at the specified index iff it is within bounds, otherwise nil.
    subscript (safe index: Index) -> Iterator.Element? {
        return indices.contains(index) ? self[index] : nil
    }
}