And_then with anyhow

I have a function like this:

fn foo<P: AsRef<Path>>(path: P) -> anyhow::Result<Foo> {
    let s = std::fs::read_to_string(path)?;
    let foo = toml::from_str(&s)?;
    Ok(foo)
}

I was wondering, since I'm using anyhow - Rust, if it is possible to make it more idiomatic using and_then:

fn foo<P: AsRef<Path>>(path: P) -> anyhow::Result<Foo> {
    std::fs::read_to_string(path).and_then(|s|  toml::from_str(&s))
}

Unfortunately read_to_string and from_str doesn't have the same result type.
Is there a way to "cast" them into anyhow::Result ?

You could probably add the From::from (or into) calls manually to convert to anyhow's result type like the ? does. But I'd say the first code sample is quite idiomatic.

2 Likes

I agree there's nothing wrong with the original. It can be rewritten as

    Ok(toml::from_str(&std::fs::read_to_string(path)?)?)

Or to

    toml::from_str(&std::fs::read_to_string(path)?).map_err(Into::into)

Or to

    std::fs::read_to_string(path)
        .map_err(Into::into)
        .and_then(|s| toml::from_str(&s).map_err(Into::into))

Which all give the same result.


You can also technically do

    std::fs::read_to_string(path)
        .and_then(|s| toml::from_str(&s).map_err(Into::into))
        .map_err(Into::into)

But this is different: toml errors get nested within an io::Error before getting type erased into an anyhow::Error, instead of directly getting type erased.

1 Like

The whole point of the ? operator is to make it easier to write error handling in the early-return style, since that's more readable in simple cases like yours. IMO you shouldn't be trying to stuff everything into the Result combinators; I'd actually prefer the first, ?-bubbling code in a code review.

3 Likes