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.