Early-breaking from Rust's match

12,260

Solution 1

You can wrap the match into a loop that only runs once and break out of the loop

fn main() {
    let x = 1;

    loop { match x {
        1 => {
            let y = 0;
            /*
             * do ev1l stuff to y that I don't want to put into the match-guard
             * as it's simply too much.
             */

            /* break early ... */
            if y == 0 { break; }

            assert!(y != 0, "y was 0!");
            /* do other stuff in here. */
        }
        _ => {}
    } break; }

    println!("done matching");
}

Solution 2

You could create a macro like

macro_rules! block {
    ($xs:block) => {
        loop { break $xs }
    };
}

and do

match x {
    1 => block!({
        ...
        if y == 0 { break; }
        ...
    })
    _ => {}
}

It's not an amazing solution, but it is semantically meaningful.

Solution 3

Something else you could do is make a "self-executing" closure and use a return statement inside. I don't know whether there are any weird performance characteristics of this but syntactically it's pretty clean.

fn main() {
    let x = 1;

    // This closure is just used to catch the "return" statement.
    (|| {
        match x {
            1 => {
                let y = 0;
                /*
                 * do ev1l stuff to y that I don't want to put into the match-guard
                 * as it's simply too much.
                 */

                /* break early ... */
                if y == 0 { return; } // Ok!

                assert!(y != 0, "y was 0!");
                /* do other stuff in here. */
            }
            _ => {}
        }
    })();

    println!("done matching");
}

Here's a playground link showing it working.

Share:
12,260
ljrk
Author by

ljrk

Pronouns: He/Him (Leo), They/Their or She/Her (Janis) https://keybase.io/larkey

Updated on June 04, 2022

Comments

  • ljrk
    ljrk almost 2 years

    I want to switch through many possible cases for x and there's one case (here x == 0) where I want to check the result of some additional code to determine what to do next. One possibility is to return early from the match.

    I'd use break to do this early-returning in C, but this isn't allowed in Rust. return returns from the parent function (in this case main()) and not from the match only (i.e. the println! at the end isn't run!).

    I could just negate the sub-condition (here y == 0) and indent the whole lot of following code -- but I find this ugly and unreadable.

    Putting the sub-condition into a match-guard is no option for me since it's simply too big.

    Is this possible in Rust or is there a better alternative (except creating another subfunction or other work-arounds)?

    Minimal example:

    fn main() {
        let x = 1;
    
        match x {
            1 => {
                let y = 0;
                /*
                 * do ev1l stuff to y that I don't want to put into the match-guard
                 * as it's simply too much.
                 */
    
                /* break early ... */
                if y == 0 {break;} // > error: `break` outside of loop [E0268]
    
                assert!(y != 0, "y was 0!");
                /* do other stuff in here. */
            }
            _ => {}
        }
    
        println!("done matching");
    }
    

    I found Mixing matching, mutation, and moves in Rust — is it wrong?

    match embraces both imperative and functional styles of programming: you can continue using break statements, assignments, et cetera, rather than being forced to adopt an expression-oriented mindset.

  • ljrk
    ljrk almost 8 years
    That's an interesting approach but I think rather obfuscates the idea more than it should and is not exactly an answer to my question. Have an upvote but I cannot accept it ;)
  • Shepmaster
    Shepmaster about 6 years
    any weird performance characteristics — there should not be as I expect this to be covered by Rust's "zero-cost abstractions".
  • don bright
    don bright about 4 years
    why doesn't it answer your question? it lets you break out of the match right? im asking because im porting a big C 1000+ line switch statement and this is pretty much how im dealing with breaking out of the middle of a match
  • otoomey
    otoomey about 3 years
    @donbright This solution works, but I think it's misleading code. If I came across it in the wild, I would have first assumed that the match is executed multiple times, when this is not the case. It would take me quite a while to figure this out, after which I would almost definitely try to refactor the loop statement out - assuming it to be a relic left by a previous fix.
  • don bright
    don bright about 3 years
    i get it now, thank you Camp bell. if i wasnt porting C i would never use this trick... i'd just rewrite it to make more sense in the first place.
  • Veedrac
    Veedrac over 2 years
    @yolenoyer I believe my answer predates that syntax. I'll update.
  • Magix
    Magix almost 2 years
    Great idea, it reminds me of those javascript self-executing closure tricks to isolate parts of the code !