Search code examples
swiftgenericscollectionsrangeswift-extensions

Understanding Swift Index, Range, Distance


I'm implementing an extension to Swift's CollectionType that provides the ability to find a subsequence in the collection and to find the range of that subsequence. My code that's working in a playground is this:

extension CollectionType where Generator.Element:Equatable, Index:ForwardIndexType, SubSequence.Generator.Element == Generator.Element {
    func search<S: CollectionType where S.Generator.Element == Generator.Element, S.Index:ForwardIndexType>(pattern: S) -> Self.Index? {
        return self.lazy.indices.indexOf{
            self[$0..<self.endIndex].startsWith(pattern)
        }
    }

    func rangeOf<S: CollectionType where S.Generator.Element == Generator.Element, S.Index:ForwardIndexType, Index:ForwardIndexType>(pattern: S) -> Range<Index>? {
        if let start = self.search(pattern) {
            var end = start
            for _ in pattern.startIndex..<pattern.endIndex {
                end = end.advancedBy(1)
            }
            return start..<end
        } else {
            return nil
        }
    }
}

Simple playground test cases are these:

let fibs = [1, 1, 2, 3, 5, 8, 13]
if let fidx = fibs.search([3, 5]) {
    print(fibs[..<fidx])                          // prints "[1, 1, 2]\n"
    print(fidx..<fidx.advancedBy([1,1,5].count))  // prints "3..<6\n"
}
if let rng = fibs.rangeOf([5,8,13]) {
    print(rng)                                   // prints "4..<7\n"
}

However, in the rangeOf function, instead of the loop

            for _ in pattern.startIndex..<pattern.endIndex {
                end = end.advancedBy(1)
            }

I expected to be able to use the statement

            end = start.advancedBy(pattern.count, limit: self.endIndex)

or perhaps

           end = start.advancedBy(pattern.endIndex - pattern.startIndex, limit: self.endIndex)

(I do recognize that the limit parameter is redundant; omitting it makes no difference in the following.) Neither of those last two compile, with the error cannot invoke 'advancedBy' with an argument list of type '(S.Index.Distance, limit: Self.Index)'. My question is, why isn't either of these two forms acceptable? (I suppose there are other valid questions as to whether I've properly formed the constraints on types for the extension and for the functions, but since the one version works I'm ignoring that for now.)


Solution

  •  end = start.advancedBy(pattern.count, limit: self.endIndex)
    

    does not compile because the collections self and pattern need not have the same Index type.

    It compiles if you add a constraint S.Index == Index to the rangeOf() method.