error: expected `{`, found `fallback`
-->
|
12 | let Some(_) = baz() else fallback!();
| ^^^^^^^^ expected `{`
|
help: you might have meant to write this as part of a block
|
12 | let Some(_) = baz() else { fallback!() };
| + +
In C, macros can appear in any position, and are replaced by arbitrary sets of tokens. In Rust, macros do produce tokens, but they’re at least properly bracketed; more interesting is that they cannot appear in any position. A macro in type position should produce tokens that make up a type; a macro in statement position should produce statements, etc.
The position after (or including) the else in a let-else chain isn’t one of these standard locations; you can’t normally put anything there but braces, so it’s not a place macros are looked for in the first place. Could it be? Sure. Is it an improvement? …Not really? The braces are there for a reason, which is to make sure the human and the computer agree on what counts as the else clause. Enforcing that the macro expansion includes braces at compilation time helps the computer, but not really a human reader.
I’ve certainly wanted macro expansion to work in more places than it does, but I’m not sure this is one of them. Even if it’s not quite true, the idea that a macro always expands to a single high-level syntactical item, or a series of them, feels like a good one to me. It makes me not have to think about the macro-ness as much, and what it might do to the structure of the program that I can’t see.
Rust is generally not concerned with allowing people to remove a couple of extra tokens here or there. Just write { fallback!() }; it's fine. And it's much easier for the reader to follow what's going on because they can see the block.
It's a plus that foo!() bar!() baz!() where the foo is the if, the bar is the first block, and the baz having the else doesn't compile.
Just for a bit of context to the let_expect macro:
I’m using it with a custom enum with data, so a .unwrap!(…) wouldn’t help much here.
enum Foo {
Bar(u64, u64),
Baz{ x: u64, y: u64, z: u64 }
}
let_expect!(Foo::Bar(a, _), value1);
let_expect!(Foo::Baz{x,y,z}, value2);
dbg!(a, x, y, z);
// Not sure which is more readable, given that the output variable "a"
// is now after the value itself. This one also might not even work.
value1.expect!(Foo::Bar(a, _));
In practice I’m not really interested in the panic message (this is in unit tests, I just wanted to print the value somewhere when it fails) and more interested in the pattern destructuring itself and keeping the call-site concise => The macro.