Search code examples
rustrust-macros

Comma separated list of syn::Idents


I want to add some syn::Ident's to form a &'static str. All Idents should be concat to form a comma separated and stringified (because that's how I get a string representation of an Ident) &'static str.

I got a non-working project here: https://github.com/Jasperav/comma_separated (inside the example folder there is a failing test).

This is my test:

#[test]
fn test_another_struct() {
    assert_eq!("name, another_name", AnotherStruct::useless())
}

It fails because of this:

Expected :nameanother_name Actual :name, another_name

This is my code trying to generated the comma separated list (inside lib.rs):

let idents = vec![
    syn::Ident::new("name", Span::call_site()),
    syn::Ident::new("another_name", Span::call_site())
];

proc_macro::TokenStream::from(quote! {
    impl #struct_name {
        fn useless() -> &'static str {
            concat!(#(stringify!(#idents)),*)
        }
    }
})

I don't understand why it's not working. There is 1 , in my macro, that means: separate everything with a comma right?


Solution

  • If you cargo expand your code, you would realize you expanded into this:

    (suppose struct_name is Foo)

    impl Foo {
        fn useless() -> &'static str {
            concat!(stringify!(name), stringify!(another_name))
        }
    }
    

    stringify! converts the ident into string directly, so the returned value is equivalent to

    concat!("name", "another_name")
    

    Here, comma is a separator for the syntax, not a string literal, so you end up getting nameanother_name.

    Only considering your code above, the most convenient fix is to formulate the raw string inside proc_macro code instead of relying on stringify! and concat!, allowing you to use APIs like slice::join.