behnam
1
I'm trying to define a macro that takes a module name as input, as :path, and access its items.
Unfortunately, format_args macro, and therefore print/println, do not let me use a :path metavariable in their args, expecting :tt.
macro_rules! something {
( $path:path ) => (
//println!("Say: {}", $path::say);
format_args!("Say: {}", $path::say);
);
}
mod foo {
const say: &str = "Hello";
}
mod bar {
const say: &str = "World";
mod baz {
const say: &str = "Universe";
}
}
fn main() {
something!(foo);
something!(bar);
something!(bar::baz);
}
It fails with three instances of this error (with RUSTFLAGS='-Z external-macro-backtrace'):
error: expected token: `,`
--> src/main.rs:4:9
|
1 | / macro_rules! talk {
2 | | ( $mod:path ) => (
3 | | // print!("Say: {}", $mod::say);
4 | | format_args!("Say: {}", $mod::say);
| | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
5 | | );
6 | | }
| |_- in this expansion of `talk!`
...
21 | talk!(foo);
| ----------- in this macro invocation
I'm surprised to see that $path::say doesn't resolve into a tt before being passed to the internal macro call. Isn't this how path metavariables are expected to be used?
Or maybe the limit here is the format_args implementation? If so, what input token type can format_args use to address this limit?
I couldn't find a answer looking at the Macros docs. Any ideas?
I believe this is a limitation in the expression parser -- it doesn't know how to combine a previously parsed $:path metavariable with a suffix like ::say to come up with a single joined path. You can reproduce it with a macro macro_rules! e { ($e:expr) => {} } and calling e!($path::say). File an issue if you can't find one already open about this, because it should be reasonably easy to support $path::say.
For now two possible workarounds would be parsing the joined path all at once from tokens by matching $($path:tt)+ and passing $($path)+::say, or continuing to match a $:path but then using it indirectly through an import like { use $path as base; base::say }.
3 Likes
behnam
3
Thanks, @dtolnay. Yeah, makes sense. I'll make sure it's reported, then!
I was going for the first workaround you mentioned, but I think the second one is even easier! Thanks for the hint! 
behnam
4
Couldn't find any related open issue. Filed this: https://github.com/rust-lang/rust/issues/48067.
1 Like