[swift-evolution] Swift 3 Ranges

Haravikk swift-evolution at haravikk.me
Tue Sep 6 05:41:55 CDT 2016

> On 6 Sep 2016, at 07:39, David Hart via swift-evolution <swift-evolution at swift.org> wrote:
> I have the impression we exchanged flexibility for correctness (the ability to represent 0..<Int.max) and that it's wasn't worth the loss of flexibility.1
> Or am I missing something?
> On 6 Sep 2016, at 08:15, David Hart via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>> Hi people,
>> I’ve recently started migrating some Swift 2 projects to Swift 3. I came across the split of Range into Range and ClosedRange and I’ve really struggled with it. Specifically, in Swift 2, I had a struct with a Range property that was initialised in many places with either a closed or open range:
>> struct Day { … }
>> struct Day : Comparable { … }
>> struct Day : Strippable { … }
>> struct Info {
>>     let name: String
>>     let range: Range<Day>
>> }
>> Info(name: "Christmas Vacation", range: twentyfith...thirtyfirst)
>> Info(name: "Summer Vacation", range: someday..<otherday)
>> Now, in Swift 3, it seems like we’ve lost a type to represent any range to allow an API client the flexibility to specify it as he wishes. Is there a solution to this problem through a protocol which both ranges conform to, or are we stuck with this because of the new API?
>> protocol RangeType {
>>     associatedtype Bounds
>>     let lowerBound: Bound { get }
>>     let upperBound: Bound { get }
>>     // what else? not even sure if it is possible to define such a protocol
>> }
>> David.

The problem was that to implement both generically, a closed range was just an open range with the upper bound increment by one step, but as you say this wasn't safe as Int.max etc. could not be used.

I think if you wanted to solve this you'd need to reintroduce the concept of incrementing by a minimum step, which was moved away from index types during the changes to the new indexing model for collections. You could do this something like so:

// Reintroduce these methods that were lost in the new indexing API
protocol ForwardStep { func successor()? -> Self }
protocol BackwardStep : ForwardStep { func predecessor()? -> Self }

// Enable conversion of ranges
extend ClosedRange where Self.Bound : ForwardStep {
	func toOpenRange() -> Range<Self.Bound>? {
		guard let upperBound = self.upperBound.successor() else { return nil }
		return self.lowerBound ..< upperBound
extend Range where Self.Bound : BackwardStep {
	func toClosedRange() -> ClosedRange<Self.Bound>? {
		guard let upperBound = self.upperBound.predecessor() else { return nil }
		return self.lowerBound ... upperBound
extend ClosedRange where Self.Bound : BackwardStep {
	init(_ openRange:Range<Self.Bound>)? {
		guard let closedRange = openRange.toClosedRange() else { return nil }
		self = closedRange
extend Range where Self.Bound : ForwardStep {
	init(_ closedRange:Range<Self.Bound>)? {
		guard let openRange = closedRange.toClosedRange() else { return nil }
		self = openRange

I've rushed this a bit so forgive any glaring errors, but this is essentially how I'd bolt this on right now myself. Basically it reintroduces some of the flexibility, but with the safety of optionals to avoid the previous problem of incrementing a maximum value, this shouldn't be a problem for performance since it's just being used for conversion.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160906/4bb37b71/attachment.html>

More information about the swift-evolution mailing list