std::Result API idea: `recover_err`

Hi all, this is a method that I've wished for a long time that Result had on it, and I thought I might bring this up here to see if anyone has thoughts, to see if there's maybe something obvious I'm missing. If people think this seems like a really good idea I might make an RFC for it.

Sometimes you have a task that ends up with Result<(), Error>, wherein sometimes the error indicates an actual problem that should be logged as an error, but sometimes it just represents something normal. For example, in quinn, accepting a stream on a connection returns Result<Stream, ConnectionError>--but if the client closes the connection intentionally then the accept method will return Err(ConnectionError::ApplicationClose), which is not actually an error and shouldn't be logged as an error.

This means that a sub-task that accepts streams on a connection will naturally have a return signature Result<(), ConnectionError>, and when it returns you want to pattern match on the error and log the error unless the error is ApplicationClose.

If there was some method on Result<I, E> that worked sort of like and_then but in "reverse" (it has the ability to turn an Err variant into an Ok variant) this could be done without pattern matching:

impl<I, E> Result<I, E> {
    fn recover_err<F: FnOnce(E) -> Result<I, E>>(self, f: F) -> Result<I, E> {
        match self {
            Ok(i) => Ok(i),
            Err(e) => f(e),
        }
    }
}
async fn accept_streams(conn: &Connection) -> Result<(), ConnectionError> { ... }

...

accept_streams(&conn).await
    .recover_err(|e| if matches!(e, ConnectionError::ApplicationClose) { Ok(()) } else { Err(e) })?;

Thoughts?

This is just or_else.

1 Like

Hah I've discovered or/or_else a few times in my rust journey (about a year in). For some reason I had a hard time keeping that API in L1 :slight_smile:

2 Likes

Ah, haha, thanks :smile: I'm always a little bit fuzzy on stdlib helper methods added after like 2019

or_else has existed since before Rust 1.0 though.

1 Like

Rust loves symmetry. For every "and", there's a corresponding "or". This is true for Option, Result, Iterator, and even things like {hash,btree}_map::Entry.

It's not that easy, actually, so there's a tool for that - Which Option/Result combinator should I use? a helping hand.

3 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.