Delta_Fore
Delta_Fore

Reputation: 3271

What is the idiomatic way of using an if-let binding when matching a `Result` and still being able to capture the error?

fn lines_from_file<F>(filename: F) -> Result<io::Lines<BufReader<File>>, io::Error>
where
    F: std::convert::AsRef<std::path::Path>,
{
    let file = File::open(filename)?;
    Ok(io::BufReader::new(file).lines())
}

fn main() {
    let filename: &str = "input.pdl";
    // This works fine
    match lines_from_file(filename) {
        Ok(lines) => {
            for line in lines {
                println!("{:?}", line);
            },
        }
        Err(e) => println!("Error {:?}", e),
    }
}

I'd like to use this instead:

if let lines = Ok(lines_from_file(filename)) {
    for line in lines {
        println!("{:?}", line);
    }
} else {
    println!("Error {:?}" /*what goes here?*/,)
}

But that gives an error:

| if let lines = Ok(lines_from_file(filename)) {
|                ^^ cannot infer type for `E`

What is the idiomatic way of using an if-let binding when matching a Result and still being able to capture the error?

Upvotes: 24

Views: 29738

Answers (1)

Lukas Kalbertodt
Lukas Kalbertodt

Reputation: 89016

[...] using an if-let binding when matching a Result and still being able to capture the error?

This is fundamentally impossible with one if let. The if let construct's only purpose is to make life easier in the case where you only want to destructure one pattern. If you want to destructure both cases of a Result, you have to use match (or multiple if let or unwrap(), but this is not a good solution). Why you don't want to use match in the first place?

Regarding your compiler error: you added the Ok() on the wrong side:

if let Ok(lines) = lines_from_file(filename) { ... }

This is the correct way of using if let: the destructuring pattern to the left, the expression producing a value to the right.

Upvotes: 35

Related Questions