2 · Model and validate · 25 MIN
Result and error propagation
Result separates a successful value from a recoverable failure.
parse returns a Result because input text may not represent the requested type. map_err translates the parsing error into the function’s chosen error type, and ? returns early on failure. The caller then decides how to present the error. This keeps low-level parsing separate from user-facing behavior and allows the same function to be tested without printing. String errors are sufficient for this small exercise; larger libraries usually define typed error enums so callers can distinguish failure categories reliably.
Use ? only in a function whose return type can carry the failure.
Read the example
fn quantity(text: &str) -> Result<u32, String> {
let n = text.parse::<u32>().map_err(|_| "invalid integer".to_string())?;
if n > 1000 { return Err("too large".to_string()); }
Ok(n)
}
fn main() {
for text in ["3", "-1", "1001"] {
match quantity(text) { Ok(n) => println!("{n}"), Err(error) => println!("{error}") }
}
}Check the expected output
3 invalid integer too large
Your challenge
Replace String errors with a typed enum and add tests for each error category.
Solution cost: O(m) parsing for m input characters. time · Account for collection storage separately from the returned result. space
Common trap
unwrap converts a recoverable input failure into a panic.
Study the project implementation
fn quantity(text: &str) -> Result<u32, String> {
let n = text.parse::<u32>().map_err(|_| "invalid integer".to_string())?;
if n > 1000 { return Err("too large".to_string()); }
Ok(n)
}
fn main() {
for text in ["3", "-1", "1001"] {
match quantity(text) { Ok(n) => println!("{n}"), Err(error) => println!("{error}") }
}
}Further reading: Official documentation
Next lesson: Iterators, vectors and map entries →