Error Handling
Chapter 6's Option<T> handled absence. This chapter handles the other "might not work" case: an operation that can fail with a reason why — and, genuinely more than most mainstream languages, Rust's approach here has real common ground with Go's own explicit error handling.
Result<T, E>
Same basic shape as Option, but the failure case carries a value (E) explaining what went wrong, not just an absence.
Rust making errors part of the function's return type, rather than an exception, is genuinely the same core philosophy Go's own (value, error) convention already uses — a real similarity, not just a contrast, distinguishing both languages from JavaScript/Python's try/catch exception model.
panic! vs. Recoverable Errors
panic! immediately stops the program — reserved for genuinely unrecoverable situations: a real bug, a broken invariant, something that should structurally never happen. Result is for expected, recoverable failure conditions: a missing file, invalid user input, a failed network call. Rust's convention is clear about which to reach for: Result for anything a caller might reasonably want to handle; panic! only for "this should never happen."
The ? Operator
Go's error handling requires an explicit check after every single call that can fail: if err != nil { return err }. Rust's ? operator does the exact same propagation in one character: if the expression is Err, it returns that Err immediately from the current function; if it's Ok, it unwraps the value and execution continues.
| Go | Rust | |
|---|---|---|
| Error type | A separate error return value | Err(E) variant of Result<T, E> |
| Propagating an error up | if err != nil { return err } — repeated at every call | ? — one character, same call site |
unwrap() and expect()
.unwrap() extracts the Ok/Some value directly — or panics if it's actually Err/None. .expect("message") does the same, with a custom panic message. Both are genuinely useful for quick prototypes or cases that are truly certain to succeed — but reaching for them on an operation that could realistically fail in production turns a recoverable situation into a full program crash.
? is best understood as a more concise expression of the exact same idea Go's repeated if err != nil already embodies — not a fundamentally different philosophy.
.unwrap() everywhere during early development, since it's shorter than a full match. The real risk: any call that genuinely can fail in production (parsing user input, a network request, a file read) will crash the entire program the moment it does, instead of being handled gracefully. Reserve bare .unwrap() for prototypes, tests, or situations that are truly structurally impossible to fail — everywhere else, use match, ?, or at minimum .expect("a clear message") so a failure at least explains itself before the crash.
Coding Challenges
Write a function parse_age(input: &str) -> Result<u8, String> that attempts to parse a string into a u8, returning a clear error message on failure. Call it with both a valid and an invalid input, handling both with match.
Rewrite this chapter's calculate function (which chains two divide calls using ?) as if Rust had no ? operator at all — using explicit match statements for each call instead. Compare the length and readability to the ? version.
📄 View solutionExplain when panic! is the right choice versus Result, using one concrete example of each — and explain what's risky about calling .unwrap() on a Result that comes from parsing user-provided input.
📄 View solutionChapter 7 Quick Reference
- Result<T, E> — Ok(T) for success, Err(E) for failure with an explanatory value
- panic! — for unrecoverable bugs/invariant violations, stops the program immediately
- Result — for expected, recoverable failures (bad input, a missing file, a failed request)
- ? — propagates an Err immediately, or unwraps Ok and continues; the concise version of Go's repeated
if err != nil - .unwrap() / .expect("msg") — extract the value or panic; fine for prototypes/tests, risky on anything that can genuinely fail in production
- Go and Rust share a real philosophy: errors as explicit return values, not exceptions
- Next chapter: Collections & Iterators — Vec<T>, HashMap<K,V>, iterator adapters, and ownership implications of iterating