We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Closures and Iterators step 14 of 28
flat_map: expand a range spec like "1-3,7,10-12"
Expand comma-separated numbers and inclusive ranges into one vector.
pub fn expand(spec: Vec<String>) -> Vec<i64>
Process the strings in input order, then each string's comma-separated fragments from left to right. Preserve duplicates. Empty input produces an empty vector.
Fragment rules
Trim Unicode whitespace around each fragment. A valid fragment is either a
single non-negative i64 or two such bounds separated by a hyphen. Trim
whitespace around each bound too. A range includes both endpoints and must
have its lower bound less than or equal to its upper bound.
Use standard decimal i64 parsing: leading zeros and a leading + are
accepted. Spaces within a number, decimal points, underscores and values above
i64::MAX are invalid. A leading minus sign is interpreted as a separator
with a missing lower bound, so negative numbers are not supported.
The supplied parser accepts "0--0": the upper bound -0 parses as zero.
Skip invalid fragments, including empty ones and reversed ranges. An invalid fragment does not discard the rest of its input string. Inputs used for this task have a total expansion small enough to fit in memory.
| Input | Output |
|---|---|
["1-3,7,10-12"] |
[1, 2, 3, 7, 10, 11, 12] |
["5-1"] |
[] |
["3-5,x-y,9"] |
[3, 4, 5, 9] |
[" +02 - 4 , 4", "7"] |
[2, 3, 4, 4, 7] |
["-3,7-,1 2,,6"] |
[6] |
A single value or range endpoint can equal i64::MAX. An inclusive range such
as 9223372036854775806-9223372036854775807 contains those two values; do not
convert the upper endpoint to an exclusive bound by adding one.
What flat_map does
flat_map maps each input to something implementing IntoIterator, then yields
its items. This has the same flattening behavior as map(...).flatten().
It removes one level of nesting, not every level recursively.
Here is a complete example that shows the intermediate item type:
fn main() {
let nested = vec![vec![vec![1, 2], vec![3]], vec![vec![4]]];
let one_level: Vec<Vec<i32>> = nested.into_iter().flatten().collect();
assert_eq!(one_level, vec![vec![1, 2], vec![3], vec![4]]);
let numbers: Vec<i32> = one_level.into_iter().flatten().collect();
assert_eq!(numbers, vec![1, 2, 3, 4]);
}
The mapping closure may return a vector, a range or another IntoIterator
value. An empty vector contributes no items. Option also implements
IntoIterator: Some contributes one item and None contributes none.
Option itself is not an Iterator. Result similarly yields its Ok value
or no items for Err, discarding the error.
When a closure returns Option, filter_map expresses keeping only the
present values directly:
fn main() {
let words = ["7", "bad", "12"];
let numbers: Vec<i64> = words.iter()
.filter_map(|word| word.parse().ok())
.collect();
assert_eq!(numbers, vec![7, 12]);
}
That example handles single numbers. This exercise also needs ranges, which can contribute several numbers per fragment.
Why the starter does not compile
The starter creates a local String named cleaned, then returns
cleaned.split(',') from a closure. The split iterator borrows cleaned, but
that string is dropped when the closure returns. Rust rejects the escaping
borrow with E0515.
You can sometimes solve this kind of problem by collecting owned fragments
inside the closure. Here the temporary is unnecessary: spec.iter() gives
references to strings already owned by expand, and each can be split directly.
The spec vector stays alive while the iterator pipeline is consumed.
There are also two behavior problems to address:
- Removing all spaces changes the grammar:
"1 2"would become the valid number"12". Trim only at the fragment and bound edges. - Parsing each fragment as one number cannot expand ranges. Add that parsing step.
Build the pipeline in two stages
First flatten the comma-separated fragments from each string. Then map each
fragment to the numbers it represents and flatten those numbers into the output.
A helper returning Vec<i64> can use an empty vector for invalid fragments,
a one-element vector for a single number, and a collected inclusive range for
valid bounds.
split_once('-') separates a range at the first hyphen. Missing bounds fail
to parse. An upper bound with a minus sign can parse, but an actually negative
value makes the range reversed. Negative zero is the exception described above.
(lo..=hi) includes hi; (lo..hi)
does not.
Your solution must compile, match the outputs and pass Clippy with warnings treated as errors.
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Rust reference solution
Sign in to track your attempts toward unlocking the reference solution.