rust 从声明性宏中的string/int构造ident

iezvtpos  于 2023-01-26  发布在  其他
关注(0)|答案(2)|浏览(186)

我有一打板条箱,每个板条箱都有两个函数part1()part2(),我想打印这两个函数的返回值,为了不重复,我写了一个小宏,所以我的主板条箱的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);
    // list goes on..
}

有没有可能从声明性宏中的字符串或int构造一个ident,所以我可以像下面这样做?我所找到的是对于proc宏,使用额外的包,或者相反。

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);
    }
}
lskq00tm

lskq00tm1#

使用concat_idents,您可以将标识符粘合在一起,但不幸的是,它(目前?)不允许数字等非标识符。

#![feature(concat_idents)]

fn m_1() {
    println!("Hi");
}

macro_rules! m {
    ($n:ident) => {
        concat_idents!(m, $n)();
    };
}

fn main() {
    // underscore is needed to make it into an identifier
    m!(_1);
}

因此,我们需要手动列出板条箱,就像手动列出函数一样。
我认为类似下面的应该工作,但它

macro_rules! call_all {
    (@funs $crat:ident $($fun:ident)+) => {{
        $($crat::$fun();)*
    }};
    ($($crat:ident)+ # $($fun:ident)+) => {{
        // error: attempted to repeat an expression containing no syntax variables matched as repeating at this depth
        $(call_all!(@funs $crat $($fun)*);)*
        //                       ^^^^^^ 
    }};
}

fn main() {
    call_all!(tokio rand # m_1 m_2)
}
tcbh2hod

tcbh2hod2#

您可以使用seq-macro机箱来简化此操作:

fn main() {
    seq_macro::seq!(day in 1..=25 {
        println!("Day {} Part 1: {}", day, d~day::part1());
        println!("Day {} Part 2: {}", day, d~day::part2());
    });
}

遗憾的是,它不支持使用前导零(01,02,...)或使用变量表示天数的格式。第一个可以用自定义proc宏修复,第二个不可以。

相关问题