macro_rules重复匹配Token类型不符预期,宏分支匹配逻辑异常排查
问题:macro_rules递归调用时无法正确匹配标识符分支
输入代码
macro_rules! foo { ($a:ident) => { println!("Printing ident"); }; ($b:literal) => { println!("Printing literal"); }; ([ $($something:expr),+ $(,)? ]) => { println!("Printing repeat"); $( foo!($something); )* }; ($catch_all:expr) => { println!("Printing expr"); }; } fn main() { foo!(baz); foo!([1, "asd"]); foo!([baz]); }
实际输出
Printing ident Printing repeat Printing literal Printing literal Printing repeat Printing expr
问题说明
直接调用foo!(baz)能正确匹配ident分支输出Printing ident,但调用foo!([baz])时,内部递归调用foo!($something)却匹配了兜底的expr分支,输出Printing expr。需要实现列表中的不同元素(字面量、标识符、复杂表达式)能分别匹配对应分支。
原因分析
问题出在宏片段的类型捕获上:原代码中用$something:expr捕获列表元素,这会将所有元素标记为表达式类型片段。当递归调用宏时,宏匹配规则会根据片段类型而非内容匹配分支——($a:ident)分支仅能匹配标识符类型片段,无法匹配被标记为expr类型的标识符,最终只能落到兜底的expr分支。
解决方案
将列表元素的捕获从:expr改为:tt(token tree,令牌树),:tt是最通用的片段类型,能保留原始语法片段的类型信息,让递归调用时宏可以正确匹配对应分支:
macro_rules! foo { ($a:ident) => { println!("Printing ident"); }; ($b:literal) => { println!("Printing literal"); }; ([ $($something:tt),+ $(,)? ]) => { println!("Printing repeat"); $( foo!($something); )* }; ($catch_all:expr) => { println!("Printing expr"); }; } fn main() { foo!(baz); foo!([1, "asd"]); foo!([baz]); foo!([complex_stuff!()]); // 测试复杂表达式 }
修正后输出
Printing ident Printing repeat Printing literal Printing literal Printing repeat Printing ident Printing repeat Printing expr
内容的提问来源于stack exchange,提问作者lesurp
相关产品推荐
相关产品推荐

