macro expansion ignores token `for` and any following

126 Views Asked by At

Similarly to this question, I was stumped by a compiler macro expansion error.

The offending code was:

/// send a command and wait for the response
///
/// 'dirty' macro that relies on outside context
macro_rules! send_and_receive {
    ($cmd: expr, $response_pat:pat => $r_block:block) => {
        send_command(&mut port, $cmd);

        for i in 0..3 {
            match receive_response(&mut port) {
                $response_pat => $r_block,
                r => {
                    eprintln!("got response: {r:?}");
                    continue;
                }
            };
        }
        panic!("missing response");
    }
}

Where the expected arguments are an enum instance, and a 'match like' expression, like so:

send_and_receive!(
    Command::Thing { field: value /* etc */ },
    Response::ThingResponse { response_field1,  /* ... */ } => {
        do_stuff_here();
    }
);
1

There are 1 best solutions below

0
On

The issue was that the macro expands to 'loose expressions', without an enclosing block.

Adding it fixed the issue (because it creates a scope to be able to return from), like so:

macro_rules! send_and_receive {
    ($cmd: expr, $response_pat:pat => $r_block:block) => {
        {
            send_command(&mut port, $cmd);

            for i in 0..3 {
                match receive_response(&mut port) {
                    $response_pat => $r_block,
                    r => {
                        eprintln!("got response: {r:?}");
                        continue;
                    }
                };
            }
            panic!("missing response");
        }
    }
}