I am fairly new to Rust, and I am having trouble getting the following code to compile:
#![feature(trace_macros)]
fn main() {
#[derive(Debug)]
struct Inner {
value: u8
}
#[derive(Debug)]
struct Outer {
inner: Inner
}
let mut x = Outer { inner: Inner { value: 64 } };
/********/
macro_rules! my_macro {
($field_path:expr, $v:expr) => {
x.$field_path = $v;
}
}
trace_macros!(true);
// my_macro!(inner, Inner { value: 42 }); // only works with $field_path:ident
my_macro!(inner.value, 42); // expected output: x.inner.value = 42;
trace_macros!(false);
x . inner.value = 42; // works fine
assert_eq!(42, x.inner.value);
}
I am getting the following errors:
error: unexpected token: `inner.value`
--> src/main.rs:20:15
|
20 | x.$field_path = $v;
| ^^^^^^^^^^^
...
26 | my_macro!(inner.value, 42); // expected output: x.inner.value = 42;
| --------------------------- in this macro invocation
|
...
error: expected one of `.`, `;`, `?`, `}`, or an operator, found `inner.value`
--> src/main.rs:20:15
|
20 | x.$field_path = $v;
| ^^^^^^^^^^^ expected one of `.`, `;`, `?`, `}`, or an operator
...
26 | my_macro!(inner.value, 42); // expected output: x.inner.value = 42;
| --------------------------- in this macro invocation
|
...
However, trace_macro seems to be able to expand my_macro!:
note: trace_macro
--> src/main.rs:26:5
|
26 | my_macro!(inner.value, 42); // expected output: x.inner.value = 42;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: expanding `my_macro! { inner . value, 42 }`
= note: to `x . inner.value = 42 ;` <<< exactly what I am looking for
If I keep $field_path parameter as ident, I am simply getting no rules expected the token `.` , which I guess makes sense, because . is an operator.
What am I missing?
Playground link
I think the issue lies in the fact that
inner.valuecontains the dot operator.
That's exactly your issue. There isn't a single fragment specifiers that allows you to match ident and/or a field access expression. The issue with using expr (expressions) is that when expanded it essentially wraps in parenthesis, i.e. x.(inner.value), which doesn't make sense and thus why you're getting an "unexpected token `inner.value`".
However, you can indeed use ident, you just need to use repetition as well.
In short, instead of $field_path:ident then you do $( $field_path:ident ).+. Then to expand it, instead of x.$field_path then you do x. $( $field_path ).+.
macro_rules! my_macro {
($($field_path:ident).+, $v:expr) => {
x.$($field_path).+ = $v;
};
}
// Now both are allowed
my_macro!(inner.value, 42);
my_macro!(inner, Inner { value: 64 });
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With