This is from the Swift Standard Library Documentation:
relative(to:)
Returns the range of indices within the given collection described by this range expression.
Here is the method signature:
func relative<C>(to collection: C) -> Range<Self.Bound> where C : _Indexable, Self.Bound == C.Index
Along with its explanation:
Parameters
collection
The collection to evaluate this range expression in relation to.
Return Value
A range suitable for slicing collection. The returned range is not guaranteed to be inside the bounds of collection. Callers should apply the same preconditions to the return value as they would to a range provided directly by the user.
Finally, here is my test code:
let continuousCollection = Array(0..<10)
var range = 0..<5
print(range.relative(to: continuousCollection))
//0..<5
range = 5..<15
print(range.relative(to: continuousCollection))
//5..<15
range = 11..<15
print(range.relative(to: continuousCollection))
//11..<15
let disparateCollection = [1, 4, 6, 7, 10, 12, 13, 16, 18, 19, 22]
range = 0..<5
print(range.relative(to: disparateCollection))
//0..<5
range = 5..<15
print(range.relative(to: disparateCollection))
//5..<15
range = 11..<15
print(range.relative(to: disparateCollection))
//11..<15
In every case, relative(to:)
just returns the original range. What is this method supposed to do?
relative(to:)
is a requirement of the RangeExpression
protocol, which Swift's range types conform to in Swift 4.
This includes:
..<
operator (Range
& CountableRange
)...
operator (ClosedRange
& CountableClosedRange
)...
operator (PartialRangeFrom
& CountablePartialRangeFrom
)...
operator (PartialRangeUpTo
)..<
operator (PartialRangeThrough
)As the documentation states, calling relative(to:)
on a range expression with a given Collection
(where the range has bounds that match the Index
type of the collection) returns a Range
suitable for slicing that collection.
In the case of half-open ranges, the bounds just stay the same, as you've observed. However, the results will differ with other range types. For example, with closed ranges, the upper bound will need to be incremented (as it's no longer inclusive). With partial ranges, the missing lower or upper bounds need to be "filled in" by the collection's startIndex
or endIndex
respectively.
For example:
let continuousCollection = Array(0 ..< 10)
do {
let range = 0 ..< 5 // CountableRange
print(range.relative(to: continuousCollection)) // 0..<5
}
do {
let range = 0 ... 5 // ClosedCountableRange
print(range.relative(to: continuousCollection)) // 0..<6
}
do {
let range = 4... // CountablePartialRangeFrom
print(range.relative(to: continuousCollection)) // 4..<10
}
do {
let range = ..<9 // PartialRangeUpTo
print(range.relative(to: continuousCollection)) // 0..<9
}
do {
let range = ...3 // PartialRangeThrough
print(range.relative(to: continuousCollection)) // 0..<4
}
The relative(to:)
requirement of RangeExpression
then allows the standard library, amongst other things, to write a generic ranged subscript on Collection
, allowing an arbitrary collection to be subscripted with an arbitrary range type with Index
bound(s):
let continuousCollection = Array(0 ..< 10)
print(continuousCollection[0 ..< 5]) // [0, 1, 2, 3, 4]
print(continuousCollection[0 ... 5]) // [0, 1, 2, 3, 4, 5]
print(continuousCollection[4...]) // [4, 5, 6, 7, 8, 9]
print(continuousCollection[..<9]) // [0, 1, 2, 3, 4, 5, 6, 7, 8]
print(continuousCollection[...3]) // [0, 1, 2, 3]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With