Swift Ranges
Tags: swift, tutorial
Published: 2020-01-16 21:01:16

# Swift Ranges
A range is an interval of values and is defined by its lower and upper bounds. 
Two range creators `..<` for half-open ranges that don;t include their upper bound, and ... for closed ranges that include both bounds
```text
let singleDigitNumbers = 0..<10
Array(singleDigitNumbers)
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
let lowercaseLetters = Character("a")...Character("z")
lowercaseLetters
// {lowerBound "a", upperBound "z"}
```

There's also prefix and postfix variants of these operators
```text
let fromZero = 0... // PartialRangeFrom<Int>
let upToZ = ..<Character("z") // PartialRangeUpTo<Character>
```

There are five concrete implementations of ranges each captures constraints on the value. Most common two are `Range` (a half-open range, `..<`) and `ClosedRange` (created using `...`). 
Both have a generic `Bound` parameter: the only requirement is that `Bound` must conform to `Comparable`.
Meaning the above `lowercaseLetters` expression is of type `ClosedRange<Character>`.

- Only a half-open range `Range` can represent an empty interval (when the lower and uppoer bounds are equal)
- Only a closed range `ClosedRange` can contain the maximum value of its element type (0...Int.max).

### Countable ranges

Ranges can be looped over and treated as a collection, however, `Character` does not conform to protocol `Strideable` because of an issue with Unicode, thus character ranges are non-iterable.
In other words, the range must be `Countable` in order for it to be iterated over. To iterate over the elements use `stride(from:to:by)` and `stride(from:through:by)` functions.

### Partial ranges

Partial ranges are created with `...` or `..<` as a prefix or a postfix operator. They are called partial because there is only the starting bound. Thus there are three kinds
```text
let fromA: PartialRangeFrom<Character> = Character("A")...
let throughZ: PartialRangeThrough<Character> = ...Character("Z")
let upTo100: PartialRangeUpTo<Int> = ..<100
```

When you iterate over a countable `PartialRangeFrom`, iteration starts with the lower bound and repeatedly calls `advanced(by: 1)`. If you use such a range in a `for` loop, you must take care to add a break condition. `PartialRangeThrough` and `PartialRangeUpTo` aren't countable.

### Expressions

All five ranges conform to `RangeExpression` protocol, which computes a fully-specified range with given constraint. Here's it's simple implementation
```text
public protocol RangeExpression {
    associatedtype Bound: Comparable
    func contains(_ element: Bound) -> Bool
    func relative<C>(to collection: C) -> Range<Bound> where C: Collection, Self.Bound == C.Index
}
``` 

For partial expressions with a missing lower bound, the `relative(to:)` method adds the collection's `startIndex` as the lower bound. For partial ranges with a missing upper bound, the method will use the collection's `endIndex`. Partial ranges enable a very compact xyntax for sclicing collections
```text
let array = [1,2,3,4] // [1, 2, 3, 4]
array[2...] // [3, 4]
array[..<1] // [1]
array[1...2] // [2, 3]
array[...] // [1, 2, 3, 4]
type(of: array[...]) // ArraySlice<Int>.Type
```

Think of `ArraySlice` as of a SQL table view. This way Swift reduces computational costs - array elements do not get copied.

Notably, `ArraySlice` conforms to `Collection` protocol, as `Array` does, so both share same methods. If you'd need to convert the view into real array
```text
Array(array[...]) // [1, 2, 3, 4]
```

This works because the corresponding subscript declaration in the `Collection` protocol takes a `RangeExpression` rather than one of the concrete range types. 
This is implemented as a special case in the standard library as *unbounded* range.

In case of making your own functions take in range, it's really advisable to copy paste from the standard library. This way clients of my APIs are always happy!