Shiklo Tools
PDF40
Image20
Calculators & Converters25
Text & Writing13
Developer26
Video14
Audio14
Document14
Data & Spreadsheet12
Web & SEO9
OCR & Scanning5
Archive & File12
Security & Privacy23
Health & Medical29
QR & Barcode5

Rust Result

Option says "there might be no value". Result says "this might have failed, and here is why". Opening a file, turning text into a number, sending a message: all of these can go wrong, and when they do, you usually want to know what happened. Result is an enum with two variants:

  • Ok(value): it worked, and here is the result.
  • Err(error): it failed, and here is the error that explains why.

You have already met one. parse returns a Result, because not every piece of text is a number.

fn main() {
    let good: Result<i32, _> = "42".parse::<i32>();
    let bad: Result<i32, _> = "forty".parse::<i32>();
    println!("{:?}", good);
    println!("{:?}", bad);
}
Output
Ok(42)
Err(ParseIntError { kind: InvalidDigit })

Handling both outcomes

Just like with Option, a match lets you handle both cases. The error value usually has a friendly message when printed with {}:

fn main() {
    for text in ["120", "12a", ""] {
        match text.parse::<u32>() {
            Ok(n) => println!("{:?} is the number {}", text, n),
            Err(e) => println!("{:?} is not a number: {}", text, e),
        }
    }
}
Output
"120" is the number 120
"12a" is not a number: invalid digit found in string
"" is not a number: cannot parse integer from empty string

Asking again instead of crashing

Up to now our input programs stopped when the user typed something wrong. With Result and a loop, we can be kinder and simply ask again:

use std::io;

fn main() {
    let age: u32 = loop {
        println!("How old are you?");
        let mut line = String::new();
        io::stdin().read_line(&mut line).expect("could not read");
        match line.trim().parse() {
            Ok(n) => break n,
            Err(_) => println!("Please type your age as a number, like 15."),
        }
    };
    println!("Thank you! You are {}.", age);
}
You type
fifteen
15
Output
How old are you?
Please type your age as a number, like 15.
How old are you?
Thank you! You are 15.

Returning your own Result

Your functions can return a Result too. The two types in the angle brackets are the type of a success and the type of an error. A String makes a simple, readable error:

fn withdraw(balance: u32, amount: u32) -> Result<u32, String> {
    if amount > balance {
        Err(format!("cannot take {} from a balance of {}", amount, balance))
    } else {
        Ok(balance - amount)
    }
}

fn main() {
    match withdraw(500, 120) {
        Ok(left) => println!("Done, {} left", left),
        Err(msg) => println!("Sorry: {}", msg),
    }
    match withdraw(500, 900) {
        Ok(left) => println!("Done, {} left", left),
        Err(msg) => println!("Sorry: {}", msg),
    }
}
Output
Done, 380 left
Sorry: cannot take 900 from a balance of 500

unwrap and expect

unwrap() takes the value from an Ok and stops the program on an Err. expect("message") does the same with your own message, which makes it much easier to see what went wrong. Use them in small experiments, or where failure really is impossible, and use match everywhere else.

Practice exercises

  1. Write a program that asks for two numbers and keeps asking until both are valid.
  2. Write fn check_password(p: &str) -> Result<(), String> that fails when the password is shorter than 8 characters.
  3. Print the error you get from parsing "3.5" as an i32, and then parse it as an f64 instead.