How to unwrap_or_continue?

I have a bunch of nested loops where I look-up for the existence of elements in collections. The lookup functions all return Option<T>. If it is a None, I will continue the inner loop. But this is making the code too far indented.

for t1 in t1_coll.iter() {
    if let Some(t1_property) = t1.get_property() {
            for t2 in t2_coll.iter() { 
            }
   } else {
   continue
}

What would be the idiomatic way to structure this? Is something like this possible, or is this a bad idea? Note that there is no "default" for the property.

let t1_property = t1.get_property().unwrap_or_continue()

Many thanks!

1 Like

You may want to use std::iter::Iterator::filter_map paired with the std::convert::identity function:

for t1 in t1_coll.iter().filter_map(std::convert::identity) {
    // `None`s will be skipped now
}

You could also write an identity closure instead, since that's shorter:

for t1 in t1_coll.iter().filter_map(|x| x)

You can also open-code an unwrap:

let t1_property = match t1.get_property() {
    Some(property) => property,
    None => continue,
};
1 Like

My version of "elvis" operator is if let Some(it) = ... { it } else:

let t1_property = if let Some(it) = t1.get_property() { it } else {
    continue;
};

but all this would be far nicer with something like guard-ed lets:

guard let Some(t1_property) = t1.get_property() else {
    continue;
};

If you need to do this often, you could also make a macro, like

macro_rules! coalesce {
    ($a:expr) => ( $a );
    ($a:expr , $($c:expr),+) => (
        if let Some(v) = $a {
            v
        } else {
            coalesce!( $($c),+ )
        }
    );
    ($($a:expr),+ ,) => (coalesce!($($a),+))
}

fn main() {
    for i in 0_usize..10 {
        let b = coalesce!(i.checked_sub(1), continue);
        dbg!((i, b));
    }
}
1 Like

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