Range Types
EssentialPrerequisites
Range expressions are literals that produce values of the Range* types from std::ops. Writing 1..5 is sugar for constructing a Range<i32> value — a real type you can store, pass, and iterate.
The range types
| Expression | Type | Meaning |
|---|---|---|
a..b | Range<T> | half-open: a ≤ n < b |
a..=b | RangeInclusive<T> | closed: a ≤ n ≤ b |
a.. | RangeFrom<T> | a ≤ n (unbounded above) |
..b | RangeTo<T> | n < b (unbounded below) |
..=b | RangeToInclusive<T> | n ≤ b (unbounded below) |
.. | RangeFull | all elements |
Range<T> and RangeInclusive<T> are the most common because they implement Iterator. The others — RangeTo, RangeToInclusive, RangeFrom, and RangeFull — exist primarily as slice indices.
Ranges as iterators
Range<T> and RangeInclusive<T> implement Iterator for integer types, so they work directly in for loops:
for i in 0..5 {
println!("{i}"); // 0, 1, 2, 3, 4
}
for i in 0..=5 {
println!("{i}"); // 0, 1, 2, 3, 4, 5
}
Because a range is an Iterator, you can call any iterator adapter on it:
let squares: Vec<i32> = (1..=5).map(|n| n * n).collect(); // [1, 4, 9, 16, 25]
let evens: Vec<i32> = (0..20).filter(|n| n % 2 == 0).collect();
RangeFrom (a..) is also an iterator that counts upward forever. Pair it with .take(n) to avoid an infinite loop:
let first_five: Vec<i32> = (10..).take(5).collect(); // [10, 11, 12, 13, 14]
Ranges as slice indices
Any slice or Vec can be indexed with a range to select a contiguous subrange. This produces a slice reference with no copying:
let v = vec![10, 20, 30, 40, 50];
let a: &[i32] = &v[1..4]; // [20, 30, 40] — Range
let b: &[i32] = &v[..3]; // [10, 20, 30] — RangeTo
let c: &[i32] = &v[3..]; // [40, 50] — RangeFrom
let d: &[i32] = &v[..]; // [10, 20, 30, 40, 50] — RangeFull
let e: &[i32] = &v[1..=3]; // [20, 30, 40] — RangeInclusive
The bounds are checked at runtime and panic if out of range.
Ranges in match
Range patterns in match use the same ..= syntax but are distinct from the Range* types — they are a feature of the pattern language, not values:
let label = match score {
0..=49 => "fail",
50..=69 => "pass",
70..=89 => "merit",
_ => "distinction",
};
A match pattern 0..=49 does not construct a RangeInclusive<i32>; it is purely a compile-time syntactic check against a numeric range.
Summary
a..bproduces aRange<T>(half-open);a..=bproduces aRangeInclusive<T>(closed).RangeandRangeInclusiveimplementIteratorfor integer types and work directly inforloops and iterator adapters...b,a..,..=b, and..are primarily used as slice indices to select contiguous subranges without copying.- Match arm patterns
a..=blook like ranges but are distinct syntax — they test values rather than constructingRangeobjects.