4

I know the idiomatic way to handle errors in rust is using match statement but I am looking for other ways to do the same.

use std::fs;

fn main() {
    if let Ok(f) = fs::read_dir("/dummy"){
        println!("First: {:?}", f);
    }else{
        println!("Error");
    }
    
}

This works but I need the original Result error from fs::read_dir("/dummy") to be printed in the else statement. How can I do it?

3
  • 3
    Why? There are ways you can do it but they're going to be uglier and more verbose than just using match. E.g. more if lets like this let f = fs::read_dir(..); if let Ok(good) = f {}; if let Err(e) = f {}) or a functional style like fs::read_dir(...).and_then(|f| println!(...) ).or_else(|e| println!(...)) . Commented Oct 7, 2020 at 6:33
  • 4
    The whole point of the if let construct is to be simpler than match when you don't need the details for the other branches. If you need those details, you will have to use match. Commented Oct 7, 2020 at 6:34
  • I don't know why everyone is saying to use match. It is so annoying to go yet another indentation level just for checking an error. if let is great and lets me use the ? operator in places I can't just return. Commented May 19, 2024 at 1:13

3 Answers 3

5

Generally, I'd consider such an approach a bad idea, but you do have a few options, the most obvious two being "multiple if lets" and a "functional style" approach. I've included the match version for comparison. The code is available on the playground.

fn multiple_if_lets() {
    let f = std::fs::read_dir("/dummy");
    if let Ok(f) = &f {
        println!("First: {:?}", f);
    }
    if let Err(f) = &f {
        println!("Error: {:?}", f);
    }
}

fn functional_style() {
    std::fs::read_dir("/dummy")
        .map(|f| println!("First: {:?}", f))
        .unwrap_or_else(|f| println!("Error: {:?}", f));
}

fn match_style() {
    match std::fs::read_dir("/dummy") {
        Ok(f) => println!("First: {:?}", f),
        Err(f) => println!("Error: {:?}", f),
    }
}
Sign up to request clarification or add additional context in comments.

Comments

2

Since Rust 1.65.0 (Nov. 2022), as mentioned by Mara Bos, you can do:

let Ok(f) = fs::read_dir("/dummy") else{ println!("Error"); return };
println!("First: {:?}", f);

let-else statements.

You can now write things like:

let Ok(a) = i32::from_str("123") else { return };

without needing an if or match statement.
This can be useful to avoid deeply nested if statements.

Comments

1

I'm not quite sure why you don't want to use match because it is like a better if let! But you might find it useful to use an external crate like anyhow. You can very easily propagate all errors as one type and also add context where it makes sense.

In your Cargo.toml

[dependencies]
anyhow = "1"

In your code

use std::fs;
use anyhow::Context;

fn main() -> anyhow::Result<()> {
    let dir = fs::read_dir("/dummy").context("failed to read dir")?;

    // another fallible function which can return a `Result` with a 
    // different error type.
    do_something(dir).context("failed to do something")?;

    Ok(())
}

If read_dir had to fail here your program would exit and it would output the following

Error: failed to read dir

Caused by:
    No such file or directory (os error 2)

If you wanted to throw away the error but still print it out you could still use match.

let dir = match fs::read_dir("/dummy").context("failed to read dir") {
    Ok(dir) => Some(dir),
    Err(err) => {
        eprintln!("Error: {:?}", err);
        None
    }
};

This would output something like the following but still continue:

Error: failed to read dir

Caused by:
    No such file or directory (os error 2)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.