Reputation: 57
I was experimenting with declarative macros and I thought about recreating F# sequence expressions using them. First attempt triggered the compilation error:
macro_rules! genexp {
(for $item:ident in $range:expr -> $expr:expr) => {
$range.map(|$item| $expr)
}
}
`$range:expr` is followed by `->`, which is not allowed for `expr` fragments
allowed there are: `=>`, `,` or `;`rustc
However, following code compiles and works just fine:
macro_rules! genexp {
(for $item:ident in [$range:expr] -> $expr:expr) => {
$range.map(|$item| $expr)
}
}
fn main() {
let foo = genexp!(for x in [0..20] -> x * x);
println!("{:?}", foo.collect::<Vec<_>>())
}
//OUTPUT: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256, 289, 324, 361]
My question is, why? Macros By Example section in Rust Reference states that:
expr
andstmt
may only be followed by one of:=>
,,
, or;
.
I see no info about special treatment of various brackets or other delimiters.
Upvotes: 2
Views: 240
Reputation: 117681
$range:expr ->
The Rust language could be expanded in the future where ->
becomes a valid part of an expression. The idea is that macros are unambiguously future-compatible with updates to Rust, so this is disallowed to be conservative.
[$range:expr] ->
The Rust language will never be expanded with features that involve unbalanced brackets/parents/braces [](){}
. Therefore, when the Rust parser sees [
it can scan for the matching ]
immediately. It is thus unambiguous which portion of this macro is the expression, and where the expression stops and the ] ->
occurs, even in the face of future language expansions.
Upvotes: 2