Option/Result interaction with break/continue

I'm still getting use to Rust's combinators for dealing with looping/option/result, and I'm finding interaction with break/continue to be ugly. I have a tool that supports passing regexes on the command line for filtering files. I found myself writing variations of this:

        let basename_str = path
            .file_name() // basename
            .and_then(|x| x.to_str());
        if basename_str.is_none() {
            continue;
        }
        let basename_match = basename_regex.is_match(basename_str.unwrap());

        let path_str = path.to_str();
        if path_str.is_none() {
            continue;
        }
        let path_match = path_regex.is_match(path_str.unwrap());

        if basename_match || path_match {
            // ...snip
        }

I don't like the unwrap calls because I'm losing the guarantee of validity I would get by having an if let or a match, but nested if lets push my code to the right of the screen, and match won't bail early if the first to_str fails (probably not hugely important in this example but you can imagine putting more expensive to check conditions after cheaper to check ones). I can't use ? for shortcutting from anything because it will return, not continue.

I just overall get the feeling I'm doing this suboptimally. Is there a better way?

Use let-else which has been stabilized in 1.65:

let Some(path_str) = path.to_str() else { continue };
let path_match = path_regex.is_match(path_str);

playground

6 Likes

It might not be the best idea, but just throwing it in for completeness - if you don't need break, only continue, you can extract the loop body into immediately-invoked closure and use the question mark as usual:

// Just some contrived method to get `Option` depending on iteration.
fn half(i: i32) -> Option<i32> {
    (i % 2 == 0).then(|| i / 2)
}

fn main() {
    for i in 0..3 {
        (|| {
            println!("Iteration start: {i}");
            let halved = half(i)?;
            println!("Iteration end: {halved}");
            Some(())
        })();
    }
}

Playground

5 Likes

Is the immediately invoked closure considered idiomatic? It looks syntactically very noisy.

The less-noisy version on nightly is try_blocks - The Rust Unstable Book

2 Likes