将嵌套结构字段路径作为宏参数传递
我是 Rust 的新手,我在编译以下代码时遇到了问题:
#![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);
}
我收到以下错误:
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
|
...
但是,trace_macro
似乎能够扩展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
如果我将$field_path
参数保持为ident
,我只是得到no rules expected the token `.`
,我想这是有道理的,因为.
是一个运算符。我错过了什么?
游乐场链接
回答
我认为问题在于
inner.value
包含点运算符的事实。
那正是你的问题。没有允许您匹配和/或字段访问表达式的单个片段说明符。使用( expressions )的问题在于,当展开它时,它本质上是用括号括起来的,即,这没有意义,因此为什么会得到.ident
expr
x.(inner.value)
"unexpected token `inner.value`"
但是,您确实可以使用ident
,您只需要使用重复即可。
简而言之,而不是$field_path:ident
然后你做$( $field_path:ident ).+
。然后扩展它,而不是x.$field_path
然后你做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 });