DEV Community

Subesh Yadav
Subesh Yadav

Posted on

πŸ“Š Day 16 of #100DaysOfRust β€” Handling Recoverable Errors with `Result<T, E>`

Rust encourages developers to explicitly handle errors and provides powerful tools to manage them safely. After exploring unrecoverable errors with panic! in Day 15, today I explored the comprehensive mechanisms around recoverable errors using Result<T, E>. This blog post covers every nuance, code pattern, and real-world scenario to help you master this vital Rust concept.


πŸ”Ή Understanding Result<T, E> Enum

enum Result<T, E> {
    Ok(T),
    Err(E),
}
Enter fullscreen mode Exit fullscreen mode
  • T is the success type.
  • E is the error type.
  • Common in operations like file handling, networking, parsing, etc.

πŸ–‰ Basic Example: Opening a File

use std::fs::File;

fn main() {
    let greeting_file_result = File::open("hello.txt");
}
Enter fullscreen mode Exit fullscreen mode
  • File::open returns Result<File, std::io::Error>.
  • Tells us clearly if the file open succeeded or failed.

πŸ“ Match for Handling Result

use std::fs::File;

fn main() {
    let greeting_file = match File::open("hello.txt") {
        Ok(file) => file,
        Err(error) => panic!("Problem opening the file: {error:?}"),
    };
}
Enter fullscreen mode Exit fullscreen mode

Output if the file doesn't exist:

Problem opening the file: Os { code: 2, kind: NotFound, message: "No such file or directory" }
Enter fullscreen mode Exit fullscreen mode

πŸ”  Matching Different Error Types

use std::fs::File;
use std::io::ErrorKind;

fn main() {
    let greeting_file = match File::open("hello.txt") {
        Ok(file) => file,
        Err(error) => match error.kind() {
            ErrorKind::NotFound => File::create("hello.txt")
                .unwrap_or_else(|e| panic!("Problem creating the file: {e:?}")),
            _ => panic!("Problem opening the file: {error:?}"),
        },
    };
}
Enter fullscreen mode Exit fullscreen mode
  • Enables more control: retry, fallback, or recover logic based on specific error types.

πŸ”„ Cleaner with unwrap_or_else

let greeting_file = File::open("hello.txt").unwrap_or_else(|error| {
    if error.kind() == ErrorKind::NotFound {
        File::create("hello.txt").unwrap_or_else(|error| {
            panic!("Problem creating the file: {error:?}");
        })
    } else {
        panic!("Problem opening the file: {error:?}");
    }
});
Enter fullscreen mode Exit fullscreen mode

🚩 Shortcuts for Panic on Error: unwrap() and expect()

let greeting_file = File::open("hello.txt").unwrap();
Enter fullscreen mode Exit fullscreen mode
let greeting_file = File::open("hello.txt")
    .expect("hello.txt should be included in this project");
Enter fullscreen mode Exit fullscreen mode
  • Prefer expect in production to give context for the panic.

🧳 Propagating Errors Manually

fn read_username_from_file() -> Result<String, io::Error> {
    let username_file_result = File::open("hello.txt");
    let mut username_file = match username_file_result {
        Ok(file) => file,
        Err(e) => return Err(e),
    };
    let mut username = String::new();
    match username_file.read_to_string(&mut username) {
        Ok(_) => Ok(username),
        Err(e) => Err(e),
    }
}
Enter fullscreen mode Exit fullscreen mode

❓ Propagating Errors Using ?

fn read_username_from_file() -> Result<String, io::Error> {
    let mut username_file = File::open("hello.txt")?;
    let mut username = String::new();
    username_file.read_to_string(&mut username)?;
    Ok(username)
}
Enter fullscreen mode Exit fullscreen mode

Even Shorter:

File::open("hello.txt")?.read_to_string(&mut username)?;
Enter fullscreen mode Exit fullscreen mode

Shortest:

use std::fs;
fn read_username_from_file() -> Result<String, io::Error> {
    fs::read_to_string("hello.txt")
}
Enter fullscreen mode Exit fullscreen mode

🌟 ? Operator Rules

  • Can only be used in functions returning Result, Option, or types implementing FromResidual.
fn main() {
    let greeting_file = File::open("hello.txt")?; // ❌ will not compile
}
Enter fullscreen mode Exit fullscreen mode

Fix:

fn main() -> Result<(), Box<dyn Error>> {
    let greeting_file = File::open("hello.txt")?;
    Ok(())
}
Enter fullscreen mode Exit fullscreen mode

🧠 ? with Option<T>

fn last_char_of_first_line(text: &str) -> Option<char> {
    text.lines().next()?.chars().last()
}
Enter fullscreen mode Exit fullscreen mode
  • Extracts early on None
  • Powerful for chaining fallible operations

πŸ€– Final Notes

  • Use match or unwrap_or_else for fine control.
  • Prefer expect over unwrap in production.
  • Use ? for clean propagation.
  • Handle different error kinds with ErrorKind.
  • main can return Result<(), Box<dyn Error>> for flexibility.

This completes a comprehensive tour of recoverable error handling in Rust. These tools help write reliable and user-friendly applications.


Stay tuned for Day 17 β€” we will explore Generic Type.

Top comments (0)