🎓 A1Learner
🔍

Rust Result

Result is the value Rust uses for an operation that can fail. A Result is either Ok, with a value, or Err, with a description of what went wrong.

Two sides: Ok and Err

A Result holds either a success value or an error — never both, and never neither:

let good: Result<u32, &str> = Ok(7);
let bad: Result<u32, &str> = Err("no number");
println!("{good:?}");
println!("{bad:?}");

match handles both sides

A match on a Result must cover Ok and Err. Nothing is thrown — the failure arrives as an ordinary value:

let result: Result<u32, &str> = Err("empty");
match result {
    Ok(n) => println!("got {n}"),
    Err(e) => println!("failed: {e}"),
}

parse turns text into a Result

Reading a number from text can fail, so parse hands back a Result. The ::<u32> spelling that names the type is called the turbofish:

let n = "42".parse::<u32>();
println!("{n:?}");

map changes Ok, map_err changes the error

Each method touches one side only, and leaves the other side alone:

let ok = "42".parse::<u32>().map(|v| v + 1);
println!("{ok:?}");
let err = "x".parse::<u32>().map_err(|_| "not a number");
println!("{err:?}");

and_then chains steps that can fail

map alone would nest one Result inside another. and_then feeds the Ok value into the next fallible step, and the answer stays one layer flat:

let total = "20".parse::<i32>().and_then(|a| "3".parse::<i32>().map(|b| a + b));
println!("{total:?}");

A type alias shortens a Result you write often

An alias names the whole type once, and signatures use the short name. std::io::Result is an alias in exactly this style:

type ParseResult<T> = Result<T, std::num::ParseIntError>;
fn parse_age(s: &str) -> ParseResult<u32> {
    s.parse::<u32>()
}

Example

fn parse_age(s: &str) -> Result<u32, std::num::ParseIntError> { s.trim().parse::<u32>() }
fn main() {
    println!("{:?}", parse_age("42"));
    println!("{:?}", parse_age("abc"));
    println!("{}", parse_age("abc").unwrap_or(18));
}

Key points

  • There are no exceptions in Rust — failure travels as a normal value
  • A panic is different: it stops the thread, and is only for bugs that should never happen

In this example, parse_age tries to read a number from text. "42" works, so the result is Ok(42). "abc" cannot be a number. So the result is Err with a parse error describing the problem. And nothing crashes. The last line shows a calm way to handle the failure. unwrap_or(18) supplies a default age. The program prints 18 and carries on.

Note

Libraries choose their own error type E.

Result values therefore often differ in their Err side from function to function.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Rust Playground

Exercise

When should you use panic! instead of Result?