Pre-RFC: Simple way to convert Result<T, T> -> T (unwrap_either?)

If I have a Result<T, T> it is always possible to get out a T from that. But there is currently no convenient way of doing that.

My current use case is migrating from Atomic*::compare_and_swap to Atomic*::compare_exchange[_weak]. The old return type was just the old value in the atomic. But with compare_exchange[_weak] the return value is Result<T, T> where both the Ok and Err variants contain the old value. For some code I just need to get the old value from this operation. See for example: https://github.com/rust-lang/rust/blob/593fe977a77ad5a7aec23c6cb0f86a3470221670/library/std/src/sync/mpsc/oneshot.rs#L267

I propose adding either a special unwrap method for this:

 impl<T> Result<T, T> {
    pub fn unwrap_either(self) -> T {
        match self {
            Ok(t) => t,
            Err(t) => t,
        }
    }
} 

Or doing it via the From trait:

impl<T> From<Result<T, T>> for T {...}

Since the Ok(_) version of compare_exchange always has a known value (the value of the current argument) it would have been possible to do .unwrap_err_or(current) on my result. But unwrap_err_or does also not exist. And there is also no method for converting Result<T, E> -> Result<E, T> that I can find.

Any way around this problem for my current situation, or would it be a good idea to add some way to easily convert Result<T, T> -> T?

You can use Result::unwrap_or_else, either via

(...).unwrap_or_else(|x| x)

or like this

(...).unwrap_or_else(identity)

with std::convert::identity if you prefer that.

-> Demonstration

3 Likes

Lol. Great! I completely missed that unwrap_or_else took an FnOnce(E) -> T I assumed/thought it was an FnOnce() -> T. Have not been using it enough. Thank you :tada:

I still think Result<T, T> -> T should be easier. But maybe the use case is too exotic :man_shrugging:

Btw, another possibility is (...).map_or_else(identity, identity).

The (IMO terribly named) map_or_else function is kind-of “almighty” in that you can basically define any operation consuming a Result by value just with map_or_else.

4 Likes

For comparison, the either crate does provide a method called into_inner with this kind of functionality for the Either type (which is pretty similar to Result).

1 Like

With #![feature(or_patterns)] you can write:

let Ok(x) | Err(x) = my_result;
x
6 Likes

in addition to the above or_patterns within match doesn't require the feature, so let x = match my_result { Ok(x) | Err(x) => x}; alas rustfmt always wants to split up the lines. I think its easy to also just add yourself an extension trait, something like

trait Uniformity {
    type Inner;
    fn uniformity(self) -> Self::Inner;
}

impl<T> Uniformity for Result<T, T> {
    type Inner = T;
    fn uniformity(self) -> T {
        match self { Ok(x) | Err(x) => x}
    }
}

I know I can just match it. That's what my example implementation of unwrap_either does. The main problem is that it's very bulky and takes up many lines. I do not look for a solution that is short only on the premise of breaking idiomatic formatting (rustfmt <3).

Adding a trait is only relevant if this is a very common operation in a project. If you have one or a few Result<T, T> -> T conversions it does not make much sense IMO.

The or_pattern is interesting. But it's still not shorter or simpler in a single expression than the already suggested unwrap_or_else(|x| x) which I went with in the end.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.