We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Errors Are Values step 22 of 24
Collecting an iterator of Results
Parse a list of strings in two ways: stop at the first error in one pass, and keep every successful value in the other.
pub fn strict_and_lenient(raw: Vec<String>) -> (Result<Vec<i32>, String>, Vec<i32>, usize)
Return these three results:
- Strict: all parsed values in
Ok, or the first failure asErr("bad input: {original}"). - Lenient: all successfully parsed values, including those after the strict pass's first failure, in input order. Preserve duplicates.
- Calls: the number of strings visited by the strict parsing closure, including the string that failed. Do not count the lenient pass.
Both passes use s.trim().parse::<i32>(). trim removes leading and trailing
Unicode whitespace recognized by Rust. The remaining text must be an optional
ASCII + or - followed by ASCII decimal digits, with a value in the i32
range. Leading zeros are valid; internal spaces, underscores and non-ASCII
digits are not. Empty or whitespace-only strings fail.
The error message includes the original, untrimmed string, exactly as
received. For " x ", the message is "bad input: x ", with two spaces
after the colon and one at the end.
For ["1", "2", "x", "4", "5"], return Err("bad input: x"),
[1, 2, 4, 5], and 3. If every string parses, calls equals the input length.
Empty input returns (Ok(vec![]), vec![], 0).
Collect successes into one Result
An iterator yielding Result<T, E> can be collected into Result<Vec<T>, E>.
Collection stops requesting items when it encounters an Err. If all items
succeed, the result contains their values in order.
This complete example counts the actual calls made to its mapping closure:
fn main() {
let raw = ["1", "x", "3"];
let mut calls = 0;
let parsed: Result<Vec<i32>, _> = raw.iter().map(|s| {
calls += 1;
s.parse::<i32>()
}).collect();
assert!(parsed.is_err());
assert_eq!(calls, 2);
}
The mapping closure runs as the iterator is consumed, not when it is created.
In your implementation, increment calls inside that closure and use
map_err to replace a parse error with the required message.
The grader checks the returned count. That catches the starter's behavior, but a self-reported number is not independent instrumentation of arbitrary code. Keep the count tied to actual parsing visits rather than calculating the expected number after parsing everything.
A loop that exits at the first failure could also implement this behavior.
Here, practice expressing it through collect::<Result<Vec<_>, _>>().
Keep only successes in the lenient pass
Convert each parse result to an Option with .ok(), then use filter_map
to keep the successful values:
fn main() {
let raw = [" 1 ", "bad", "-2", "1"];
let values: Vec<i32> = raw.iter()
.filter_map(|s| s.trim().parse::<i32>().ok())
.collect();
assert_eq!(values, [1, -2, 1]);
}
This visits the full input. It deliberately discards error details, while the
strict pass preserves the first error message. There is no need to filter by
is_ok() and then unwrap the same results.
The destination changes the behavior
Vec<Result<T, E>> retains each result, including errors. Result<Vec<T>, E>
stops at the first error and does not return the partial success vector.
If context does not choose a destination, add a type annotation or turbofish.
Option has a similar collection rule: an Option<Vec<T>> becomes None
at the first missing item. By contrast, flattening an iterator of options
skips missing items and continues. Option and Result implement
IntoIterator; they are not themselves Iterator implementations.
fn main() {
let items = [Some(1), None, Some(3)];
let strict: Option<Vec<i32>> = items.into_iter().collect();
let lenient: Vec<i32> = items.into_iter().flatten().collect();
assert_eq!(strict, None);
assert_eq!(lenient, [1, 3]);
}
What the starter gets wrong
The starter records the first error but continues parsing the rest of the input. Its strict result and lenient values are correct, but its strict call count is too large when a failure occurs before the final item. If the last item fails, or no item fails, that count alone does not expose the problem.
Move the strict work into a lazy mapping iterator and collect it into a
Result. Then perform the lenient pass separately, without incrementing
the strict counter.
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.