Search code examples
rustmacrosrust-macros

Stringify variable arguments in Rust macro with delimiter


Given a variable number of arguments in a Rust macro (i.e., $($varargs:pat),*), I want to print them nicely delimited.

For example, given a macro something! that takes a variable number of arguments, e.g. something!(a, b, c), I want to print them in a string: "a, b, c".

I could not find any resources online for this, so putting it out into the world in case this helps someone else. Other answers appreciated.


Solution

  • Joining Vector Solution

    macro_rules! something {
        ($($varargs:pat),* $(,)?) => {
            println!("{}", something!(@stringify_varargs $($varargs),*));
        };
    
        (@stringify_varargs $($varargs:pat),*) => {
            {
                vec![$(format!("{}", stringify!($varargs))),*].join(", ")
            }
        };
    }
    
    fn main() {
        let a = 1;
        let b = 2;
        let c = 3;
        something!(a, b, c);
    }
    
    

    Mutating String Solution

    macro_rules! something {
        ($($varargs:pat),* $(,)?) => {
            println!("{}", something!(@stringify_varargs $($varargs),*));
        };
    
        (@stringify_varargs $($varargs:pat),*) => {
            {
                let mut patterns = String::new();
                $(
                    patterns.push_str(&format!("{}, ", stringify!($varargs)));
                )*
    
                // Remove the trailing comma
                let _ = patterns.truncate(patterns.len() - 2);
                patterns
            }
        };
    }
    
    fn main() {
        let a = 1;
        let b = 2;
        let c = 3;
        something!(a, b, c);
    }
    
    

    Recursive Solution

    macro_rules! something {
        ($($varargs:pat),* $(,)?) => {
            println!("{}", something!(@stringify_varargs $($varargs),*));
        };
    
        (@stringify_varargs $($expected:pat),*) => {
            something!(@stringify_varargs_helper $($expected),*)
        };
        (@stringify_varargs_helper $first:pat, $($rest:pat),*) => {
            concat!(stringify!($first), $(", ", stringify!($rest)),*)
        };
        (@stringify_varargs_helper $last:pat) => {
            // Do not print last comma
            stringify!($last)
        };
    }
    
    fn main() {
        let a = 1;
        let b = 2;
        let c = 3;
        something!(a, b, c);
    }