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.

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.