rustmacrosrust-decl-macros

How to concat two variables to create an identifier in a declarative macro?


I want to write a macro to support parameterized tests, and got the following code from AI, but got errors on one line:

#[macro_export]
macro_rules! parameterize {
    ($name:ident, $params:pat, {$($test_name:ident : ($($args:expr),*)),*}, $test_body:block) => {
        $(
            fn $name_\$test_name() {   //<========= this line
                let $params = ($($args),*);
                $test_body
            }
        )*
     };
}

parameterize! {
     should_be_added,
     (expected, a, b),
     {
         positive: (3, 1, 2),
         zero: (0, 0, 0),
         negative: (-5, -2, -3),
         large_numbers: (999999, 444444, 555555)
     },
     {
         println!("a={}, b={}, expected={}", a, b, expected);
     }
}

fn main() {
   positive();
   negative();
}

If I change fn $name_\$test_name() to fn $test_name() , it works well.

So I want to know how to concatenate the two variables $name $test_name to output one string. For example, if $name is foo and $test_name is bar, how to output foo_bar?


Solution

  • There is a crate called paste, that provides a macro for concatenating tokens inside declarative macros.

    To use it, wrap your entire result in the paste! macro, and then you can concatenate tokens by placing them between [< and >] with spaces to separate.

    use paste::paste;
    
    #[macro_export]
    macro_rules! parameterize {
        ($name:ident, $params:pat, {$($test_name:ident : ($($args:expr),*)),*}, $test_body:block) => {
            paste! {
                $(
                    fn [<$name _ $test_name>]() { // <- special syntax used by paste!
                        let $params = ($($args),*);
                        $test_body
                    }
                )*
            }
         };
    }
    

    Using the remainder of the code you've provided, this would generate functions should_be_added_positive and should_be_added_negative.