How to process an Option<T> with a function that returns Result<T,E>, so that the final result will be an Option<T> again?

Hi!

Suppose I have an Option<T> that I want to process by another function, iff there is some value.

The final result shall be an Option<T> again!

 

If the other function takes a T and returns an Option<T> then that is easy:

let result = optional_value.and_then(other_function);

 

But what if the other function (not my function) happens to take a T but returns a Result<T,E> ???

Sure, I can do the following, but it seems a bit convoluted:

let result = optional_value.map(other_function).map(Result::ok).flatten();

Alternatively, I can do the following, but again not the nicest solution:

let result = optional_value.and_then(|val| other_function(val).ok());

Is there a better, more "idiomatic" solution?

What I'd need is an and_then() that accepts Result<T,E> and implicitly maps it to an Option<T>.

Thank you!

I didn't find any nicer solution either, and it's probably how I would do it (to avoid the double wrapping). But I'm also curious if there's a better solution.

2 Likes

Merely a rearrangement and not necessarily nicer:

let result = optional_value.map(other_function).transpose().ok().flatten();

But if you're in an early-return situation you could do:

fn foo(optional_value: Option<String>) -> Option<String> {
    other_function(optional_value?).ok()
}

fn bar(optional_value: Option<String>) -> Option<String> {
    optional_value.map(other_function)?.ok()
}

Or with try blocks (unstable):

let result = try { other_function(optional_value?).ok()? };
let result = try { optional_value.map(other_function)?.ok()? };
2 Likes

When I work a lot with errors and have a bunch of similar cases where I need treatment that in a certain way, I sometimes define extension traits for my needs (which I use locally inside a module or crate).

You could use that to define your own and_then_res_ok method, for example:

trait OptionResultExt<T> {
    fn and_then_res_ok<U, F, E>(self, op: F) -> Option<U>
    where
        F: FnOnce(T) -> Result<U, E>;
}

impl<T> OptionResultExt<T> for Option<T> {
    fn and_then_res_ok<U, F, E>(self, op: F) -> Option<U>
    where
        F: FnOnce(T) -> Result<U, E>,
    {
        match self {
            Some(t) => match op(t) {
                Ok(u) => Some(u),
                Err(_) => None,
            }
            None => None,
        }
    }
}

fn other_function(arg: i32) -> Result<i32, ()> {
    Ok(arg / 2)
}

fn main() {
    let optional_value = Some(9);
    let result = optional_value.and_then_res_ok(other_function);
    println!("result = {result:?}");
}

(Playground)

Output:

result = Some(4)

This might increase or reduce code readability though, depending on the perspective.

P.S.: Not sure what's the best name for such a method. Maybe and_then_ok is better?

3 Likes

It’s only a small usability improvement, but the need to specify the type signature twice can be avoided using a macro for extension traits :slight_smile:

1 Like

Your and_then_res_ok function seems like exactly what I was looking for :sweat_smile:

Maybe we could call it just and_try()?

Too bad that this apparently is not in the standard library. Seems like a standard use-case...

Unfortunately, I need an actual Option<T> value, not early-return.

try block looks like a great addition :+1:

It might be possible to provide a general implementation (for any "early return" type) when the try_trait_v2 stabilizes, but not sure. These try/?-related traits always confuse me. :face_with_spiral_eyes:

I should take some time to understand them better, but not sure if they will make their way into stable eventually.