Ranges in Swift

Its actually pretty simple.

Let’s first get a possible misconception out of way. If you have spent anytime at all with Swift, you might know that … denotes a closed range and ..< denotes a half-range. So 1..5 must be of the type ClosedRange and 1..<5 must be of the type Range. Except, that they are not.

The below snippet shows the actual types.

let range1 = "A"..."D"  // ClosedRange<String>
let range2 = "A"..<"D" // Range<String>
let range3 = 1...4 // CountableClosedRange<Int>
let range4 = 1..<4 // CountableRange<Int>

These 4 are all the Range types that are currently there in Swift. If the interval’s end points conform to Comparable protocol, they belong to the non-Countable family and if they conform to Strideable protocol and their Stride type is integer, they belong to the Countable family. Hence 1…4 is of type CountableClosedRange, but 1.0…4.0 is of type ClosedRange.

All the 4 types can be checked if they contain an element. This can be done either using the contains method or using the ~= operator (also known as pattern matching operator). Both the method as well as operator are implemented by each of the 4 range types.

let isInRange = range1.contains("e")
let isInRange = (range4 ~= 3)

As only the items of Countable types conform to Strideable protocol, can they be iterated and in fact treated almost like collections.

for item in range4  { print(item) }    // This will compile
for item in range2 { print(item) } // This will not compile
range4.filter { $0%2 == 0 } // This will compile

And a few minor things. If you wanted to create an empty range you do it using a half-open range (of either type). And be careful about the bounds, do not flip them. Like, ever.

1..<1    // An empty range
6..<4 // You would wish this didn't compile. It instead crashes!