Stringify variable arguments in Rust macro with delimiter

77 Views Asked by At

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.

1

There are 1 best solutions below

0
Jake Ireland On BEST ANSWER

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