4

Is there a canonical way to convert Option<Result<T>> to Result<Option<T>>?

fn optres_to_resopt<T>(optres: Option<Result<T>>) -> Result<Option<T>> {
    if let Some(res) = optres {
        match res {
            Ok(v) => Ok(Some(v)),
            Err(e) => Err(e),
        }
    } else {
        Ok(None)
    }
}
Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135

2 Answers2

7

There is Option::transpose:

fn optres_to_resopt<T>(optres: Option<Result<T>>) -> Result<Option<T>> {
    optres.transpose()
}

And BTW, the implementation is basically your function:

pub fn transpose(self) -> Result<Option<T>, E> {
    match self {
        Some(Ok(x)) => Ok(Some(x)),
        Some(Err(e)) => Err(e),
        None => Ok(None),
    }
}
hellow
  • 12,430
  • 7
  • 56
  • 79
edwardw
  • 12,652
  • 3
  • 40
  • 51
3

I would definitely use transpose(from the approved answer) but as an exercise here is a different version of implementation:

fn optres_to_resopt<T, E>(optres: Option<Result<T, E>>) -> Result<Option<T>, E> {
    optres.map_or(Ok(None), |x| Ok(Some(x?)))
}

Playground

Since Result also has a transpose method too, it is easy to revert it back. As an alternative we can apply the same approach in this problem too:

fn resopt_optres<T, E>(resopt: Result<Option<T>, E>) -> Option<Result<T, E>> {
    resopt
        .map(|x| Some(Ok(x?)))
        .unwrap_or_else(|x| Some(Err(x)))
}

Playground

Note : The idea in here is map_or(or unwap_or_else) has ability to change the enclosing type with a generic type.

Ömer Erden
  • 7,680
  • 5
  • 36
  • 45
  • 1
    I like this answer, but just for your interest, you can simplify those a bit: https://stackoverflow.com/a/43166454/365102 – Mateen Ulhaq Jan 29 '20 at 13:29
  • @MateenUlhaq aah, i didn’t want to use closure inside a closure, but i guess i’ve totally forgot that we can use tuples as a closure argument, thanks ^^ – Ömer Erden Jan 29 '20 at 13:35