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

声明式宏嵌套调用函数式过程宏报错,如何解决?能否正常运行?

问题:声明式宏间接调用过程宏导致数组长度不匹配错误

尝试通过声明式宏arr!间接调用函数式过程宏arr_proc!,而非直接调用arr_proc!,但编译时出现类型不匹配错误,预期长度为2的数组,实际得到长度为0的数组,疑似编译器未完成所有宏展开。请问该用法是否可行?如何解决?


过程宏代码

#[proc_macro]
pub fn arr_proc(input: TokenStream) -> TokenStream {
    let repeat_expr: ExprRepeat = parse(input)
        .expect("Like arr!([Test::default(); 16])");

    let mut len = 0;
    // 获取长度
    if let Expr::Lit(expr_lit) = repeat_expr.len.deref() {
        if let Lit::Int(int_lit) = &expr_lit.lit {
            len = int_lit.base10_parse::<usize>().expect("Failed to parse integer literal");
        }
    }
    let _expr = repeat_expr.expr;
    // 生成数组元素
    let mut _all = quote!();
    for _i in 0..len {
        _all = quote! { #_all #_expr, };
    }
    // 包裹成数组
    let arr = quote! { [ #_all ] };
    arr.into()
}

声明式宏代码

#[macro_export]
macro_rules! arr {
    ($ele:expr; $repeat:literal) => {
        custom_proc_macro::arr_proc!([$ele; $repeat])
    };
}

测试用例

#[test]
fn test_arr_() {
    let a: [u32; 2] = arr![1_u32;2];
    dbg!(a);
}

错误信息

error[E0308]: mismatched types
  --> tests/custom_proc_macro_test.rs:48:23
   |
48 |     let a: [u32; 2] = arr![1_u32;2];
   |            --------   ^^^^^^^^^^^^^ 预期固定长度为2的数组,实际得到长度为0的数组
   |            |     |
   |            |     提示:可以考虑指定实际数组长度:`0`
   |            因此处类型标注导致预期
   |
   = note: 预期数组 `[u32; 2]`
              实际数组 `[_; 0]`
   = note: 该错误源自宏 `custom_proc_macro::arr_proc`,它由宏 `arr` 展开而来(在Nightly版本中,使用-Z macro-backtrace查看更多信息)

解决方案

这种用法完全可行,错误根源是过程宏的解析逻辑存在问题,而非宏展开机制的限制。

核心问题分析

声明式宏传递给arr_proc!的输入是[$ele; $repeat],这是一个数组字面量(ExprArray),但过程宏中错误地将其解析为重复表达式(ExprRepeat),导致解析失败后len保持初始值0,最终生成空数组[]。

修复方案

方案1:修改过程宏以支持数组字面量输入

调整过程宏的解析逻辑,先识别数组字面量,再提取其中的重复表达式部分:

use syn::{parse_macro_input, Expr, ExprArray, ExprRepeat, Lit, ExprLit};
use quote::quote;

#[proc_macro]
pub fn arr_proc(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    // 先解析为通用表达式
    let expr = parse_macro_input!(input as Expr);
    
    let (elem_expr, len) = match expr {
        Expr::Array(arr) => {
            // 检查是否是 [elem; len] 格式的数组
            if arr.elems.len() == 1 {
                match arr.elems[0].as_ref() {
                    Expr::Repeat(repeat) => {
                        // 提取长度字面量
                        let len = match repeat.len.as_ref() {
                            Expr::Lit(ExprLit { lit: Lit::Int(int_lit), .. }) => {
                                int_lit.base10_parse::<usize>().expect("长度必须是整数字面量")
                            }
                            _ => panic!("长度必须是整数字面量"),
                        };
                        (repeat.expr.clone(), len)
                    }
                    _ => panic!("数组内必须是 elem; len 格式的重复表达式"),
                }
            } else {
                panic!("仅支持 [elem; len] 格式的数组输入")
            }
        }
        _ => panic!("输入必须是数组字面量,格式为 [elem; len]"),
    };
    
    // 使用重复展开语法生成数组元素,更简洁高效
    let elements = (0..len).map(|_| &elem_expr);
    let arr = quote! { [ #(#elements),* ] };
    
    arr.into()
}

方案2:修改声明式宏的传递内容

直接将$ele; $repeat传递给过程宏,让过程宏直接解析重复表达式:

// 修改声明式宏
#[macro_export]
macro_rules! arr {
    ($ele:expr; $repeat:literal) => {
        custom_proc_macro::arr_proc!($ele; $repeat)
    };
}

// 对应简化后的过程宏
#[proc_macro]
pub fn arr_proc(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let repeat_expr = parse_macro_input!(input as ExprRepeat);
    
    let len = match repeat_expr.len.as_ref() {
        Expr::Lit(ExprLit { lit: Lit::Int(int_lit), .. }) => {
            int_lit.base10_parse::<usize>().expect("长度必须是整数字面量")
        }
        _ => panic!("长度必须是整数字面量"),
    };
    
    let elements = (0..len).map(|_| &repeat_expr.expr);
    let arr = quote! { [ #(#elements),* ] };
    
    arr.into()
}

验证效果

修复后,arr![1_u32;2]会被正确展开为[1_u32, 1_u32],与类型[u32;2]匹配,编译通过。


内容的提问来源于stack exchange,提问作者Colagy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 11:54:55