C.W.K.
Stream
Lesson 02 of 05 · published

Result — Recoverable Errors

~11 min · errors, result, recoverable

Level 0Rust Curious
0 XP0/80 lessons0/19 achievements
0/100 XP to next level100 XP to go0% complete

Most failures aren't bugs — a file might not exist, input might be malformed, a network call might time out. These are recoverable, and Rust models them as values with Result<T, E>.

Result, revisited

You met Result as an enum in the Enums track: Ok(T) for success, Err(E) for failure-with-a-reason. The standard library returns it from everything fallible: str::parse, File::open, every I/O call. The caller must engage with the Err case — the compiler won't let it silently slip by.

Handling a Result

You handle it with the tools you know: match on Ok/Err, or combinators like map, map_err, unwrap_or, unwrap_or_else. The key difference from Option is the E: you don't just know that it failed, you know why, and can branch on the reason.

Errors are values that flow through your code. No hidden control flow, no invisible throw three call-frames up. A function that can fail says so in its return type, and the failure travels as an ordinary value you can match, map, or propagate. You can't be surprised by an error you never declared.

The verbosity problem

Matching every Result by hand gets tedious fast — especially when a function calls five fallible things and wants to bail on the first failure. That tedium is exactly what the ? operator (next lesson) eliminates, turning a five-line match into one character.

Code

Result: success or a reason for failure·rust
use std::num::ParseIntError;

fn double(s: &str) -> Result<i32, ParseIntError> {
    match s.trim().parse::<i32>() {
        Ok(n) => Ok(n * 2),
        Err(e) => Err(e), // pass the failure reason along
    }
}

fn main() {
    match double("21") {
        Ok(n) => println!("got {n}"),
        Err(e) => println!("failed: {e}"),
    }
    // Combinator style with a fallback:
    let n = double("oops").unwrap_or(0);
    println!("{n}");
}

External links

Exercise

Write fn parse_temp(s: &str) -> Result<f64, String> that parses a float and returns a descriptive Err(String) on failure (use .map_err). Handle the result with a match in main, printing different messages for success and failure. Why is returning Result better here than panicking?
Hint
Bad user input is an expected, recoverable condition — the caller should decide what to do (re-prompt, default, log). Panicking would crash the whole program over a typo. Result hands the decision to the caller.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue
💛 by Ttoriwarm

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.