You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 05:25:40