Exploring Range Types in Swift
One of the many great things that came with Swift 3 release was the Range API on the Swift Standard Library, and since then, it was only improving. Before it, we only had the NSRange that is part of the Foundation framework.
In today’s article, I’ll try to explain what I learned about the Range, ClosedRange, PartialRangeUpTo, PartialRangeThrough and CountableRange, the differences between all of this types of range and how they can be used.
So … let's start
First, let's talk about range and then we can explore the others.
A half-open interval over a comparable type, from a lower bound up to, but not including, an upper bound.
The range type has a Bound generic type that can be any type that conforms to Comparable protocol. That means you can create ranges with Int, Double, Strings … and even your own custom types since they conform to the Comparable protocol.
From Apple Standard Library Documentation ClosedRange is …
An interval over a comparable type, from a lower bound up to, and including, an upper bound.
The only difference between the Range and ClosedRange is that the ClosedRange includes the upper bound.
Since Swift 4.1, just a typealias for Range<Bound> where Bound conforms to Strideable and Bound.Stride conforms to SignedInteger.
As you can see below in the definition of the typealias.
And by taking advantage of conditional conformance, Range conforms to Sequence, Collection, BidirectionalCollection, RandomAccessCollection where where Bound conforms to Strideable and Bound.Stride conforms to SignedInteger, meaning where is a CountableRange.
As you can see the conditional conformance definition in the Standard Library Sources on stdlib/public/core/Range.swift
So, you can work with the range as a Collection and have access to functions distance, indexes, reverse, map, filter and so on …
The same rules are applied to CountableClosedRange with the difference that is a typealias for ClosedRange<Bound> applying the same conformance to Strideable and Bound.Stride conformance to SignedInteger constraints.
And now the upper bound is now included in the Collection.
OBS: The Array transformation was only made so we can see on the playground, the range and closed are already a Collection.
A partial half-open interval up to, but not including, an upper bound.
Is the same of the Range, but it only have the upper bound.
One more time …
A partial half-open interval up to, and including, an upper bound.
Is the same of the ClosedRange, but you only have the upper bound.
I swear this is the last one …
A partial interval extending upward from a lower bound.
Is the same of the ClosedRange, but you only have the lower bound.
Contains in Range
The protocol RangeExpression, which is one of the protocols that all of these types of ranges conforms to, defines a method called contains that can verify if a value is contained within the range. Also there is the ~= operator that does the same checking.
As you can see it's very simple and very useful. If you are curious and want to see how those types are implemented under the hood, you can look into the Standard Library Sources on stdlib/public/core/Range.swift on the Swift's Repo.
There are a lot more interesting things about Ranges that I didn’t cover here, and the API is still getting better and better in all the new releases of Swift. The intention here was just to show the basics and provide examples to demonstrate how you can use it. I referenced all the links to the Apple Documentation and also a link the Swift Standard Library Range.swift.gyb Source, so you can see it for more details if you are curious.
That’s all for this one, hope you like it :)
If I got something wrong or you got some comment or question, please let me know. I’ll be really happy receiving your feedback, so I can make the next articles better :)
Thanks for reading :)