One can simply assign the value of the declarative macro vec![]
back to a declared variable, e.g.
let v = vec![1, 2, 3];
We then expect v
to be a vector of usize
.
Let's take, as an example, a possible and incomplete implementation of one of the match arms of the macro vec![]
, like so:
( $( $x:expr ),* ) => {
{
let mut v = Vec::new();
$( v.push($x); )*
v
}
};
We can see that whenever we call let v = vec![...]; // let LHS = RHS;
, the RHS get expanded into something like that of the implementation above, one can imagine the expanded codes to look something like so:
let v = let mut v = Vec::new(); v.push(1); v.push(2); ...
Of course, let v = vec![1,2,3];
compiles well.
On the other hand, for procedural macros, the expanded codes on the RHS may SIMILARLY contain statements like let length_array = /*some calculations*/
within the expanded codes and it fails to compile as the RHS of the statement contains statements instead of expression (at least that is what Rust complains about).
Which is why when I am writing procedural macros that returns a value, I have to 'return' a 'hardcoded' token (computed at compile time) back E.g. https://github.com/jymchng/median-proc-macro/blob/master/crates/median-pm-core/src/implement.rs#L75
My question is, how are declarative macros expanded such that it is capable of returning a value to the macro caller while expanding the codes and yet why procedural macros cannot really do that?