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

在Rust声明式宏中,能否从字符串/整数构造ident?

问题描述

我有十几个crate,每个都包含part1()和part2()函数,想要打印每个crate中这两个函数的返回值。为避免重复代码,我写了如下声明式宏,主crate的main()函数实现如下:

fn main() {
    macro_rules! print_day {
        ($day:ident) => {
            let day = stringify!($day).replace('d', "");
            println!("Day {day} Part 1: {}", $day::part1());
            println!("Day {day} Part 2: {}", $day::part2());
        };
    }

    print_day!(d01);
    print_day!(d02);
    print_day!(d03);
    // 后续还有更多类似调用..
}

现在我想改成通过循环调用该宏,传入整数并转换为对应的ident(比如数字1转为d01),期望实现的逻辑如下:

fn main() {
    macro_rules! print_day {
        ($day:expr) => {
            let daystr = format!("{:02}", $day);
            let dayident = ???;
            println!("Day {daystr} Part 1: {}", $dayident::part1());
            println!("Day {daystr} Part 2: {}", $dayident::part2());
        };
    }

    for day in 0..=num_days {
        print_day!(day);
    }
}

但目前找到的方案都涉及过程宏或额外依赖,请问能否在声明式宏中直接从字符串或整数构造ident实现该需求?

解答

核心结论

声明式宏无法实现从字符串/动态整数构造ident的需求,原因有两点:

  1. 声明式宏是编译期展开的,而循环中的day是运行时动态变量,编译期宏无法获取运行时的值;
  2. Rust声明式宏的语法本身不支持将字符串或动态数值转换为标识符,标识符必须是宏展开阶段就能确定的静态符号。

无额外依赖的替代方案

不用过程宏或第三方库,依然可以通过声明式宏批量生成调用,避免手动重复编写代码:

方案1:批量列举标识符

新增一层宏,一次性传入所有day的标识符,自动生成所有print_day调用:

fn main() {
    macro_rules! print_day {
        ($day:ident) => {
            let day = stringify!($day).replace('d', "");
            println!("Day {day} Part 1: {}", $day::part1());
            println!("Day {day} Part 2: {}", $day::part2());
        };
    }

    macro_rules! print_all_days {
        ($($day:ident),*) => {
            $(print_day!($day);)*
        };
    }

    // 传入所有需要调用的day标识符,按需扩展
    print_all_days!(d01, d02, d03, d04, d05);
}

方案2:递归宏生成连续标识符

如果你的day是连续编号的,可以用标准库的concat_idents!宏拼接标识符,配合递归宏自动生成连续的调用:

fn main() {
    macro_rules! print_day {
        ($day:ident) => {
            let day = stringify!($day).replace('d', "");
            println!("Day {day} Part 1: {}", $day::part1());
            println!("Day {day} Part 2: {}", $day::part2());
        };
    }

    macro_rules! print_days_recursive {
        // 递归终止条件:无剩余天数
        () => {};
        // 递归处理当前天数,再处理剩余天数
        ($current:expr, $($rest:expr),*) => {
            print_day!(concat_idents!(d, $current));
            print_days_recursive!($($rest),*);
        };
    }

    // 传入连续的编号(带前导零),自动生成d01到d05的调用
    print_days_recursive!(01, 02, 03, 04, 05);
}

concat_idents!是Rust标准库提供的宏,仅能在编译期拼接静态标识符片段,正好适配连续编号的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 23:11:14