Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a shortcut to unwrap or continue in a loop?

Consider this:

loop {
    let data = match something() {
        Err(err) => {
            warn!("An error: {}; skipped.", err);
            continue;
        },
        Ok(x) => x
    };

    let data2 = match something_else() {
        Err(err) => {
            warn!("An error: {}; skipped.", err);
            continue;
        },
        Ok(x) => x
    };

    // and so on
}

If I didn't need to assign the ok-value to data, I'd use if let Err(err) = something(), but is there a shortcut to the code above that'd avoid copy-pasting the Err/Ok branches on this, I think, typical scenario? Something like the if let that would also return the ok-value.

like image 987
Yuri Geinish Avatar asked Apr 11 '18 22:04

Yuri Geinish


2 Answers

While I think that E_net4's answer is probably the best one, I'm adding a macro for posterity in case creating a separate function and early-returning with the ? operator is for some reason undesirable.

Here is a simple skip_fail! macro that continues a containing loop when passed an error:

macro_rules! skip_fail {
    ($res:expr) => {
        match $res {
            Ok(val) => val,
            Err(e) => {
                warn!("An error: {}; skipped.", e);
                continue;
            }
        }
    };
}

This macro can be used as let ok_value = skip_fail!(do_something());

Playground link which uses skip_fail to print out numbers divisible by 1, 2, and 3, and print an error when one of the divisions would truncate.

Again, I believe that using ? in a separate function, and returning an Ok(end_result) if nothing fails, is probably the most idiomatic solution, so if you can use that answer you probably should.

like image 188
MutantOctopus Avatar answered Oct 10 '22 13:10

MutantOctopus


If you are going to "unwrap or continue" on results often, consider encapsulating that logic in a separate function. With it, you can take advantage of the ? syntax to raise errors out of the function. The loop's flow logic can then be written in a single place (although at this point, you might no longer need the continue).

loop {
    if let Err(err) = do_event() {
        warn!("An error: {}; skipped.", err);
        // continue; // you also don't need this
    }
}

fn do_event() -> Result<(), YourErrorType> {
    let data = do_something()?; // 
    let x = something_more()?;  // error propagation!
    Ok(())
}
like image 40
E_net4 stands with Ukraine Avatar answered Oct 10 '22 14:10

E_net4 stands with Ukraine