Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Ranges

A range is a span over numbers, characters, or another ordered type. Ranges show up in loops, indexing, and membership tests.

A range always contains ... Endpoints juxtapose with ... An optional first,second pattern sets the step size.

Syntax

[first..]               # first to inf
[..last]                # -inf to last
[first..last]           # first to last
[..]                    # -inf to inf

[first,second..]        # step is second - first
[first,second..last]
[..2ndlast,last]        # step is last - 2ndlast

[first..2ndlast,last] is not allowed. Use [first,second..last] instead.

The inferred step may be positive or negative. A zero step such as 1,1..10 is invalid.

Bounds are inclusive by default. Square brackets include an end; parentheses exclude it. The two ends are independent.

[first..last]   # include both
[first..last)   # include first, exclude last
(first..last]   # exclude first, include last
(first..last)   # exclude both
first..last     # same as [first..last]

Juxtaposition

An endpoint is part of the range only if it is juxtaposed with ..:

first..last     # first to last
first ..last    # -inf to last
first.. last    # first to inf
first .. last   # -inf to inf

Range juxtaposition is medium-low precedence, so first..last + 1 is first through last+1.

first..last+1
first,second..last/2
a in first..last

Numeric Ranges

(1..5)  # 2 3 4
(1..5]  # 2 3 4 5
[1..5)  # 1 2 3 4
[1..5]  # 1 2 3 4 5
1..5    # 1 2 3 4 5

A right-unbounded range such as 0.. has a first value and iterates forever. A left-unbounded range such as ..10 is a valid range value but cannot be iterated, because it has no first value. The same is true of .. and ..3,5.

Character Ranges

Unannotated string bounds use one-grapheme strings. Iteration is defined when each supplied anchor is a grapheme containing exactly one Unicode scalar. Values advance in scalar order and skip the surrogate interval.

ord_range = 'a'..'z'
alpha_range = ['a'..'z'] + ['A'..'Z']
loop letter in 'z','y'..'a' { ... }
let ascii_scalars:range<uint32> = 'A'..'Z'

Multi-scalar graphemes have no invented universal successor. Enumerating them requires an explicit alphabet or collation policy. See Strings and Graphemes.

Uses

Loops

loop i in 0..5 print'{i} '
# 0 1 2 3 4 5

loop i in 5,4..0 print'{i} '
# 5 4 3 2 1 0

A reversed range requires an explicit step. 5..0 results in an empty range.

Membership

5 in? [1..5]         # true
5 in? (1..5)         # false
3 in? (1..5)         # true

Indexing

full_string = 'this is a string'
substring = full_string[3..12]
printl(substring)    # 's is a str'

Because indexing is juxtaposition, the range's own brackets choose inclusive or exclusive ends:

full_string(3..12)   # ' is a st'
full_string[3..12)   # 's is a st'
full_string(3..12]   # ' is a str'
full_string[3..]     # 's is a string'
full_string[..12]    # 'this is a str'
full_string[..]      # the whole string

end is the index of the last element:

arr[end]       # last element
arr[end-1]     # second to last
arr[..end-3]
arr[5..end-3]
arr[end-3..]

Provisional design: Integer positions and end define ordinary sequence slicing. Indexing by noninteger ordered domains requires a collection-specific indexing contract and is not implied by the generic range syntax.

Range Arithmetic

Provisional design: Applying arithmetic to a complete range and combining several ranges are selected directions, but their result types, normalization, empty-span behavior, and runtime representation are not yet fully specified. The following examples illustrate that direction rather than defining the remaining edge cases.

loop i in [0..4]/4 print'{i} '
loop i in [0..4]*0.25 print'{i} '
# both: 0 0.25 0.5 0.75 1

This expresses the same intended values as [0,0.25..1]. Numerical libraries can provide linspace and logspace helpers without changing the range grammar.

Compound Ranges

complex_range = [1..5] + (15..20)
loop i in complex_range
    printl(i)           # 1 2 3 4 5 16 17 18 19
7 in? complex_range     # false
16 in? complex_range    # true

complex_range = [1..20) - (5..15]