Question about range checks in index(_:offsetBy:) method over Collections

The default behavior of the index method (including index(before:), index(after:) and index(_:offsetBy:)) will perform range checking. For example:

extension RandomAccessCollection /* ... */ {
  @inlinable
  public func index(before i: Index) -> Index {
    let result = i.advanced(by: -1)
    _failEarlyRangeCheck(
      result, bounds: Range(uncheckedBounds: (startIndex, endIndex)))
    return result
  }
}

However, the index method of Array is special:

extension Array {
  @inlinable
  public func index(_ i: Int, offsetBy distance: Int) -> Int {
    // NOTE: Range checks are not performed here, because it is done later by
    // the subscript function.
    return i + distance
  }
}

The comment explans that range checks are not performed here, because it is done later by the subscript function.

The point is, index does not always appear at the same time as subscript, for example:

extension Collection {
    public func split(count: Int) -> [SubSequence] {
        precondition(count >= 1,
          "Number of elements in each group must be more than 0")
        
        var result: [SubSequence] = []
        var subSequenceStart = startIndex
        let cachedEndIndex = endIndex

        while subSequenceStart < cachedEndIndex {
            let subSequenceEnd =
                Swift.min(index(subSequenceStart, offsetBy: count),
                          cachedEndIndex)
            result.append(self[subSequenceStart..<subSequenceEnd])
            subSequenceStart = subSequenceEnd
        }
        
        return result
    }
}

If we use Array to test this function, no problem will be reported, but if we use other Collection (e.g. Data) to test, the range checks will break the program.

This example shows that we do need range check here, even if this test will be repeated in the subscript.

Terms of Service

Privacy Policy

Cookie Policy