Unable to use indices.contains() in a Collection extension in Swift 3

swift, swift2, swift3

Solution

Swift 4 Update

In Swift 4, thanks to the ability to have `where` clauses on associated types, `Collection` now enforces that `Indices`'s `Element` type is the same type as the `Collection`'s `Index`.

This therefore means that we can just say:

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

The `Sequence` protocol in Swift 3 still has a `contains(_:)` method, which accepts an element of the sequence if the sequence is of `Equatable` elements:

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

The problem you're encountering is due to the change in the type of `Collection`'s `indices` property requirement. In Swift 2, it was of type `Range<Self.Index>` – however in Swift 3, it is of type `Indices` (an associated type of the `Collection` protocol):

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

As there's currently no way in Swift for the `Collection` protocol itself to express that `Indices`'s `Iterator.Element` is of type `Index` (this will however be possible in a future version of Swift), there's no way for the compiler to know that you can pass something of type `Index` into `contains(_:)`. This is because it's currently fully possible for a type to conform to `Collection` and implement `Indices` with whatever element type it wants.

Therefore the solution is to simply constrain your extension to ensure that `Indices` does have elements of type `Index`, allowing you to pass `index` into `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
    }
}

Problem

I wrote following extension in 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 } } ``` However, it turns out that Swift 3.0 does not have `contains()` function. Instead, it offers me following syntax for this method: ``` indices.contains(where: { (<#Self.Indices.Iterator.Element#>) -> Bool in <# code ??? what should it do??? #> }) ``` The problem is that I don't know what should it contain inside the block. Any help with migrating it, please?

Original source

Related problems