Reading — step 1 of 7
Learn
Result/Option Chaining and ?
Rust has no exceptions. Fallibility is encoded in ordinary values:
Option<T>—Some(T)orNone: "might be missing"Result<T, E>—Ok(T)orErr(E): "might fail, and here's why"
Because they appear in the type signature, the compiler forces every caller to deal with them. No invisible control flow, no forgotten catch blocks.
Handling: match, if let, combinators
let maybe: Option<i32> = Some(5);
match maybe {
Some(n) => println!("got {}", n),
None => println!("nothing"),
}
if let Some(n) = maybe { // when you only care about one arm
println!("got {}", n);
}
For pipeline-style code, combinators beat nested matches:
let v = maybe.unwrap_or(0); // 5, or the default on None
let v = maybe.unwrap_or_else(|| expensive()); // lazily computed default
let v = maybe.map(|n| n * 2); // Some(10) — transform inside
let v = maybe.and_then(|n| checked_div(100, n)); // chain a fallible step
The trap: unwrap()
.unwrap() means "crash if this failed":
let n: i32 = "abc".parse().unwrap();
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value:
ParseIntError { kind: InvalidDigit }'
A panic aborts the program with a nonzero exit code and nothing useful on stdout. Fine in a throwaway script; in graded exercises and production code it's exactly the failure mode you were supposed to handle.
The idiom: ? propagates
? unwraps the success case and early-returns the failure to the caller:
fn parse_two(a: &str, b: &str) -> Result<i32, std::num::ParseIntError> {
let x = a.parse::<i32>()?; // Err? return it. Ok? unwrap into x
let y = b.parse::<i32>()?;
Ok(x + y)
}
Each ? desugars to roughly:
let x = match a.parse::<i32>() {
Ok(v) => v,
Err(e) => return Err(From::from(e)), // error types convert via From
};
Division of labor: parse_two propagates with ?; its caller decides with match. That's the shape of most real Rust code — deep functions bubble errors up, and one place near the top turns them into user-facing behavior.
? only works inside a function that returns Result or Option. Use it in a fn main() that returns () and you get:
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option`
Collecting many Results into one
let nums: Result<Vec<i32>, _> = strings.iter()
.map(|s| s.parse::<i32>())
.collect(); // the first Err short-circuits the whole collect
Your exercise
Implement parse_two(a, b) using ? on both parses and returning Ok(x + y). main is already written: it matches your Result and prints sum: <n> on Ok, or error: invalid input on Err.
TEST 1 feeds 3 then 4 and expects exactly:
sum: 7
Mistakes the grader catches:
- Leaving the placeholder
a.parse::<i32>()as the return value — it ignoresbentirely, so TEST 1 printssum: 3instead ofsum: 7. - Using
.unwrap()instead of?— TEST 3 feedsabcand expectserror: invalid input; unwrap panics, the program dies, and the test sees no output at all. The whole point is returning theErrsomain's match can handle it. - Changing
main's strings — the grader matchessum: 7(colon, one space) anderror: invalid inputbyte for byte. - TEST 4 feeds
-5then5and expectssum: 0—parse::<i32>()handles the minus sign; no special-casing needed.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…