We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Ground Rules: Values, Types, Control Flow step 17 of 24
Ranges: .., ..=, and range patterns
Bucket each score into a band.
pub fn bucket(scores: &[i32]) -> Vec<String>
0..=33 is "low", 34..=66 is "mid", 67..=100 is "high", and anything
outside 0..=100 is "invalid".
The boundaries are the whole test: 33 is low, 34 is mid, 66 is mid, 67 is high, −1 and 101 are invalid.
The starter is correct and fails clippy three times for the same reason.
Two range syntaxes, and where the off-by-ones live
0..5 // exclusive: 0, 1, 2, 3, 4 type Range<i32>
0..=5 // inclusive: 0, 1, 2, 3, 4, 5 type RangeInclusive<i32>
..5 // RangeTo — slicing only
5.. // RangeFrom
.. // RangeFull
a..b is the default because it composes with lengths: 0..v.len() is exactly
the valid indices, and a..b has b - a elements. a..=b is what you want
whenever the upper bound is a real value in the domain rather than a limit —
“scores up to and including 100”, “the loop runs from 1 to n”.
Choosing wrongly here is the classic off-by-one, and Rust at least makes the
choice visible in the syntax instead of hiding it in a < versus <=.
Ranges as values and ranges as patterns are different things
This distinction causes persistent confusion, so name it once:
A range value is a struct you can iterate or ask questions of.
for i in 0..5 { … } // iterate
(0..=100).contains(&score) // query
let slice = &v[1..4]; // index with it
A range pattern appears in a match arm and matches a value inside the
interval.
match score {
0..=33 => "low",
34..=66 => "mid",
67..=100 => "high",
_ => "invalid",
}
The second one is not iterating anything and there is no range struct at
runtime. It compiles to the same comparisons you would have written by hand,
and it reads as a table. Exclusive range patterns (0..34) stabilised in Rust
1.80 and work here too.
match requires exhaustiveness: the arms must cover every possible value of
the scrutinee, and the compiler checks it. _ is the wildcard arm that catches
everything left over. Track 4 covers match in full; for this problem you only
need the table shape.
The lint: manual_range_contains
The starter writes s >= 0 && s <= 33, three times over. Clippy’s
manual_range_contains is on by default and pushes you to (0..=33).contains(&s)
— or, better here, to a match with range patterns, which expresses the whole
partition as one thing instead of three overlapping-looking conditions.
Two range traps worth carrying with you
A backwards range is empty, not reversed.
for i in 5..0 { … } // never executes. No error. No warning at runtime.
If you want to count down, say for i in (0..5).rev(). Clippy’s
reversed_empty_ranges is on by default and catches the literal case
(5..0), but it cannot see a range whose ends came from variables — so this is
one to keep in your head.
..= with an upper bound of MAX is fine, .. is not. 0..=u8::MAX
iterates all 256 values; 0..u8::MAX quietly stops at 254.
Loading visualization…
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.