Search code examples
error-handlingrustrust-result

Should I ditch using `and_then` and always use the `?` operator?


I want to write something like this, but it won't compile due to a mismatch between types:

fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
    let val = std::env::args()
        .nth(1)
        .ok_or("1 arg is expected")
        .and_then(std::fs::File::open)
        .and_then(serde_yaml::from_reader)?;
}

Because adding a map_err to every closure seems sluggish and 'boiler platy', I replace it with something like:

fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
    let val = serde_yaml::from_reader(std::fs::File::open(
        std::env::args().nth(1).ok_or("1 arg is expected")?,
    )?)?;
}

The first one feels more natural and reads like English while the second feels sort of backwards.

Should I ditch the and_then and always use the ? operator?

If not, is there a way to make result combinator as smooth as the ? operator?


Solution

  • Should I ditch the and_then and always use the ? operator ?

    That’s a personal judgement and only you can answer it.

    Is there a way to make result combinator as smooth as the ? operator ?

    Frankly speaking no. ? performs conversions « implicitly » (not really implicitly since it’s very much part of its job, but the conversion doesn’t have to be invoked separately, maybe « tersely »?), and_then does not. That means when using and_then, you have to perform these conversions yourself. That seems logical.

    You might be able to build a convenience macro for this tho. Or maybe add an extension method or wrapper type which can perform those conversions under the cover.