Return from a macro rule?

Say I have a macro like

macro_rules! any_gt {
	($v:expr $(, $es:expr)*) => {{
		$(
			if $es > $v {
				*something* true;
			}
		)*
		false
	}};
}

What can I replace the *something* with so that it stops processing and returns true?

break or return were my initial thoughts, but

  • 'break' outside of a loop, and
  • mismatched types: expected '()' because of default return type sounds like the return is actually returning from main, not the macro.

Playground

Can you wrap the body of the macro in a closure and use return?

You could use a loop and break with a value:

macro_rules! any_gt {
    ($v:expr $(, $es:expr)*) => {
        loop {
            $(
                if $es > $v {
                    break true;
                }
            )*
            break false;
        }
    };
}
1 Like

Will the closure cause any performance problems? I suppose it's possible for the compiler to optimise that case, but is it reasonable to expect that?

If so, that seems like a good solution. Otherwise loop { ... } it will have to be. I'm also wondering if either of these solutions would be considered idiomatic in rust.

What about turning it into a big if-else chain so that each branch returns a true/false?

macro_rules! any_gt {
	($v:expr $(, $es:expr)* $(,)?) => {{
	    if false { unreachable!() }
		$(
			else if $es > $v {
				true
			}
		)*
		else { false }
	}};
}

fn main() {
    assert_eq!(any_gt!(10, 1), false);
    assert_eq!(any_gt!(10, 1, 2, 3, 4, 11), true);
    
}
2 Likes

I would expect the compiler to optimize them all to roughly the same code, but that’s just a guess. Though I’m not sure if there would be any risk with loop/closure to anger the borrow checker. @Michael-F-Bryan’s solution is certainly cleaner though and easier for the compiler to optimize. I guess you could take that a step further And just do this for this case.

false $( || $es > $v )*
2 Likes

Looking back, I just found a complicated way to write if true { return true } else { return false }, didn't I? :rofl:

1 Like

with else if support, though :smile:

1 Like

Thanks all! The long boolean solution should work for my case, so I'll give that a shot.

A more general solution might be useful in other scenarios, so the loop and closure are good to know as well.