如何解析 Rust 宏中的单个标记
How to parse single tokens in rust macros
我开始玩 Rust 宏,我来尝试这个小练习示例。我想定义一个宏,它扩展为 i32
类型的变量初始化(名称无关紧要)(例如,但不是很重要)以及对该变量的一系列操作,在本例中为 var += 1
或 var -= 1
最后它会调用 println!("{}", var)
。
该宏将采用一系列基于 +
和 -
的标记,这些标记与上述操作相匹配。
例如:
operate_integer![+++---]
将扩展为:
let mut var: i32 = 0;
var += 1;
var += 1;
var += 1;
var -= 1;
var -= 1;
var -= 1;
print!("{}", var);
我决定为此使用 2 个宏,一个用于包装初始化和打印,另一个用于评估 +-
标记:
基础是:
macro_rules! operate_integer {
// $($all_tokens:tt)* should match everything, it will be forward to the helper macro
($($all_tokens:tt)*) => {
let mut var : i32 = 0;
operate_integer_helper![$($all_tokens:tt)*]
print!("{}", var);
}
}
助手将扩展操作:
macro_rules! operate_integer_helper {
// the idea is that it matches a `+` followed by more tokens
(+$($t:tt)*) => {
val += 1;
operate_integer_helper![$($t:tt)*] // we recursively handle the remaining tokens
}
(-$($t:tt)*) => {
val -= 1;
operate_integer_helper![$($t:tt)*]
}
}
这当然行不通,它编译失败并出现以下错误(Playground):
error: no rules expected the token `(`
--> src/lib.rs:102:5
|
102 | (+$($t:tt)*) => {
| ^ no rules expected this token in macro call
我有点卡住了。我知道自从我刚开始我可能会遗漏很多概念,我真的很感激能帮助我理解如何使用宏。
提前致谢!
你其实非常接近!只剩下几个小错误了。 (如果您想了解有关宏的更多信息,一次只阅读一个要点,然后尝试从那里自己进步!)
使用(重复的)元变量时,无需再次指定元变量类型。所以在宏的pattern中是$($t:tt)*
,但是如果要用的话,就是$($t)*
!
如果您在宏定义中有多个规则,您需要以分号结束每个规则。
macro_rules! {
(+ $(t:tt)*) => { ... };
(- $(t:tt)*) => { ... };
}
Rust 编译器总是需要知道您是否想将宏扩展为表达式或语句。由于您正在生成语句列表而不是单个表达式,因此您必须在宏的调用中添加分号!这意味着,在 main()
以及宏定义中对辅助宏的所有宏调用。
因为 yeah 宏调用创建了一个新的 语法上下文 并且所有标识符(名称)只能在其语法上下文中访问,所以辅助宏不能使用 var
(即使在修正拼写错误 val
-> var
之后)。因此,您必须将该名称传递给辅助宏:
macro_rules! operate_integer {
($($all_tokens:tt)*) => {
let mut var: i32 = 0;
operate_integer_helper![var $($all_tokens)*]; // <-- pass identifier
println!("{}", var);
}
}
macro_rules! operate_integer_helper {
($var:ident +$($t:tt)*) => { // <- accept identifier
$var += 1; // <- use identifier
operate_integer_helper![$var $($t)*]
};
($var:ident -$($t:tt)*) => {
$var -= 1;
operate_integer_helper![$var $($t)*]
};
}
完成所有操作后出现错误 "unexpected end of macro invocation"。这是因为你没有递归停止规则!因此,您必须向辅助宏添加一个新规则:($var:ident) => {};
。当只有名称而没有剩余 +
或 -
标记时使用此规则。
现在:有效!
我仍然会更改最后一件事:通常使用第二个辅助宏不是一个好主意,因为该宏可能不在调用主宏的范围内。相反,人们通常使用内部规则。您可以阅读更多有关 here.
的信息
我开始玩 Rust 宏,我来尝试这个小练习示例。我想定义一个宏,它扩展为 i32
类型的变量初始化(名称无关紧要)(例如,但不是很重要)以及对该变量的一系列操作,在本例中为 var += 1
或 var -= 1
最后它会调用 println!("{}", var)
。
该宏将采用一系列基于 +
和 -
的标记,这些标记与上述操作相匹配。
例如:
operate_integer![+++---]
将扩展为:
let mut var: i32 = 0;
var += 1;
var += 1;
var += 1;
var -= 1;
var -= 1;
var -= 1;
print!("{}", var);
我决定为此使用 2 个宏,一个用于包装初始化和打印,另一个用于评估 +-
标记:
基础是:
macro_rules! operate_integer {
// $($all_tokens:tt)* should match everything, it will be forward to the helper macro
($($all_tokens:tt)*) => {
let mut var : i32 = 0;
operate_integer_helper![$($all_tokens:tt)*]
print!("{}", var);
}
}
助手将扩展操作:
macro_rules! operate_integer_helper {
// the idea is that it matches a `+` followed by more tokens
(+$($t:tt)*) => {
val += 1;
operate_integer_helper![$($t:tt)*] // we recursively handle the remaining tokens
}
(-$($t:tt)*) => {
val -= 1;
operate_integer_helper![$($t:tt)*]
}
}
这当然行不通,它编译失败并出现以下错误(Playground):
error: no rules expected the token `(`
--> src/lib.rs:102:5
|
102 | (+$($t:tt)*) => {
| ^ no rules expected this token in macro call
我有点卡住了。我知道自从我刚开始我可能会遗漏很多概念,我真的很感激能帮助我理解如何使用宏。 提前致谢!
你其实非常接近!只剩下几个小错误了。 (如果您想了解有关宏的更多信息,一次只阅读一个要点,然后尝试从那里自己进步!)
使用(重复的)元变量时,无需再次指定元变量类型。所以在宏的pattern中是
$($t:tt)*
,但是如果要用的话,就是$($t)*
!如果您在宏定义中有多个规则,您需要以分号结束每个规则。
macro_rules! { (+ $(t:tt)*) => { ... }; (- $(t:tt)*) => { ... }; }
Rust 编译器总是需要知道您是否想将宏扩展为表达式或语句。由于您正在生成语句列表而不是单个表达式,因此您必须在宏的调用中添加分号!这意味着,在
main()
以及宏定义中对辅助宏的所有宏调用。因为 yeah 宏调用创建了一个新的 语法上下文 并且所有标识符(名称)只能在其语法上下文中访问,所以辅助宏不能使用
var
(即使在修正拼写错误val
->var
之后)。因此,您必须将该名称传递给辅助宏:macro_rules! operate_integer { ($($all_tokens:tt)*) => { let mut var: i32 = 0; operate_integer_helper![var $($all_tokens)*]; // <-- pass identifier println!("{}", var); } } macro_rules! operate_integer_helper { ($var:ident +$($t:tt)*) => { // <- accept identifier $var += 1; // <- use identifier operate_integer_helper![$var $($t)*] }; ($var:ident -$($t:tt)*) => { $var -= 1; operate_integer_helper![$var $($t)*] }; }
完成所有操作后出现错误 "unexpected end of macro invocation"。这是因为你没有递归停止规则!因此,您必须向辅助宏添加一个新规则:
($var:ident) => {};
。当只有名称而没有剩余+
或-
标记时使用此规则。
现在:有效!
我仍然会更改最后一件事:通常使用第二个辅助宏不是一个好主意,因为该宏可能不在调用主宏的范围内。相反,人们通常使用内部规则。您可以阅读更多有关 here.
的信息