Use ? operator inside map function

Please consider the following example:

fn t() -> Option<()> {
    let a = [Some(0), None, Some(2)];
    let mut iter = a
                    .into_iter()
                    .map(|x| x?)
                    .for_each(|x| println!("{}", x));
    Some(())
}

fn main() {
    println!("{}", t().is_none())
}

I know why it is not compiled, however, I am looking for a solution to make it possible to use ? operator in these cases.

1 Like

What is it that you actually want to do? The following compiles:

let mut iter = a
                .into_iter()
                .map(|x| { Some((*x)?) })
                .for_each(|x| println!("{:?}", x));

but it isn't very useful. The above turns the &Option<i32> into an Option<i32> via *x, returns None if that results in None, and otherwise wraps the value in Some, so it's essentially .map(|x| *x).

I wanted to unwrap() all elements in map (or return from t function if an element is None), then use the unwraped elements in for_each

A normal for loop seems just as ergonomic here:

for x in a.into_iter().cloned() {
    println!("{}", x?);
}                    
3 Likes

I believe you are looking for the filter_map iterator adapter. This method will filter out all elements that return None and keep those that return Some(_) and returns the item inside the Some(_).

It can be used like this:

fn t() -> Option<()> {
    let a = [Some(0), None, Some(2)];
    let iter = a.into_iter()
        .filter_map(|x| *x)
        .for_each(|x| println!("{}", x));
    Some(())
}

fn main() {
    println!("{}", t().is_none())
}

Try on Rust Playground

2 Likes

I was going to suggest filter_map as well until I saw @yaa110 mentioning that they want to return from t() when a None is seen - that's different semantic from filter_map.

1 Like

For cases where you return Result, it's useful to know that .collect() can take iterator of Result<T,E> and collect into Result<Vec<T>, E>, so you can then use ? on the collection:

a.into_iter() 
    .map(|x| x.ok_or(NoneError))
    .collect::<Result<Vec<_>,_>>()?
10 Likes

If you're on nightly:

fn t() -> Option<()> {
    let a = [Some(0), None, Some(2)];
    a
        .iter()
        .cloned()
        .try_for_each(|x| Some(println!("{}", x?)))
}
3 Likes

Works with options too!

fn main() {
    let a = vec![Some(0), None, Some(2)];
    let xs = a.into_iter().collect::<Option<Vec<_>>>();
    println!("{:?}", xs)
}

playground

3 Likes

Update: try_for_each (& friends) are now in FCP for stabilization:

https://github.com/rust-lang/rust/issues/45594#issuecomment-375203766

3 Likes