Skip to content

← Errors Are Values step 8 of 24

Easy Primitives

Result combinators and map_err

Parse each input as an i32 and return its doubled value as a string. When parsing fails, return bad input: followed by the original input. Keep the input order and produce one output for every input, including failures.

pub fn parse_all(raw: Vec<String>) -> Vec<String>
Input Output
"20" "40"
" 5 " "10"
"12x" "bad input: 12x"
" x " "bad input: x "
"" "bad input: "
"2147483647" "4294967294"

An empty input vector produces an empty output vector. Error messages contain no added quotation marks or newline.

Trim for parsing, preserve the original for errors

Call trim() before parsing. Integer parsing does not remove surrounding whitespace itself. Use the original string in the error message so spaces and other characters are preserved.

The trimmed input must be a decimal integer in the i32 range. A leading plus or minus is accepted; decimal points, underscores and out-of-range values fail. For example, both "3.5" and "9999999999" produce error messages.

A successful parse does not guarantee that doubling fits in i32. Convert the parsed value to i64 before multiplying by two. Every doubled i32 value fits in i64; parsing directly as i64 would incorrectly accept inputs outside the requested range.

Transform success and failure separately

Parsing produces Result<i32, ParseIntError>. map transforms a successful value; map_err transforms an error. Each leaves the other variant alone.

s.trim().parse::<i32>()
    .map_err(|_| format!("bad input: {s}"))

This expression has type Result<i32, String>. The closure replaces the parse error with the exercise's message and captures s to include the input. A parse error describes the failure but does not store the original input.

Use map to turn a successful number into its doubled decimal string. Once both variants contain a String, unwrap_or_else(|error| error) can return the error string on failure and the success string otherwise. Unlike unwrap(), this does not panic simply because the result is Err.

What needs fixing in the starter

The starter converts the parse result to an Option, then returns only "bad input" on failure. That loses the required input text. The original string is still available, so discarding ParseIntError alone does not make it impossible to construct the message.

Clippy's manual_ok_err lint also flags the match that converts Ok to Some and Err to None. Replacing it with .ok() addresses that lint, but does not fix the missing text or the arithmetic overflow.

Choosing what an error should retain

This exercise intentionally returns the same message format for every parse failure. Other APIs may need to distinguish invalid digits from out-of-range values, or retain the original error as a cause. Choose that representation from the caller's needs; a user-facing message does not require discarding the underlying error everywhere else in a program.

For later exercises, and_then chains an operation that returns another Result, while or_else tries recovery after an error. An ordinary match is also useful when the branches need several steps.

Passing requires compilation, correct outputs and Clippy with warnings treated as errors.