jamone jamone - 4 years ago 197
Swift Question

Swift Range behaving like NSRange and not including endIndex

I ran across some funky

Range
behavior and now I'm questioning everything I thought I knew about
Range
in Swift.

let range = Range<Int>(start: 0, end: 2)
print(range.count) // Prints 2


Since
Range
uses a
start
&
end
instead of a
location
&
length
that
NSRange
uses I would expect the range above to have a count of 3. It almost seems like it is being treated like an
NSRange
since a count of 2 makes sense if your
location = 0
and
length = 2
.

let array = ["A", "B", "C", "D"]
let slice = array[range]


I would expect
slice
to contain
ABC
since range's end index is 2, but
slice
actually contains
AB
, which does correspond to the
range.count == 2
, but doesn't add up since the range's
endIndex == 2
which should include
C
.

What am I missing here?

I'm using Xcode 7.2's version of Swift, not any of the open source versions.

Answer Source

Range objects Range<T> in Swift are, by default, presented as a half-open interval [start,end), i.e. start..<end (see HalfOpenInterval IntervalType).

You can see this if your print your range variable

let range = Range<Int>(start: 0, end: 2)
print(range) // 0..<2

Also, as Leo Dabus pointed out below (thanks!), you can simplify the declaration of Range<Int> by using the half-open range operator ..< directly:

let range = 0..<2
print(range) // 0..<2  (naturally)

Likewise, you can declare Range<Int> ranges using the closed range operator ...

let range = 0...1
print(range) // 0..<2 

And we see, interestingly (in context of the question), that this again verifies that the default representation of Ranges are by means of the half-open operator.


That the half-open interval is default for Range is written somewhat implicitly, in text, in the language reference for range:

Like other collections, a range containing one element has an endIndex that is the successor of its startIndex; and an empty range has startIndex == endIndex.

Range conforms, however, to CollectionType protocol. In the language reference to the latter, it's stated clearly that the startIndex and endIndex defines a half-open interval:

The sequence view of the elements is identical to the collection view. In other words, the following code binds the same series of values to x as does for x in self {}:

for i in startIndex..<endIndex {
    let x = self[i]
}

To wrap it up; Range is defined as half-open interval (startIndex ..< endIndex), even if it's somewhat obscure to find in the docs.


See also

Swift includes two range operators, which are shortcuts for expressing a range of values.

...

The closed range operator (a...b) defines a range that runs from a to b, and includes the values a and b. The value of a must not be greater than b.

...

The half-open range operator (a..< b) defines a range that runs from a to b, but does not include b. It is said to be half-open because it contains its first value, but not its final value.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download