Proc_macro generating array or vec

I want to pre-generate an array of data at build time and include the final array/vec in my program using a proc_macro.

If I do this with a simple i32 it works:

#[proc_macro]
pub fn pre_calc_values(_input: TokenStream) -> TokenStream {
    let data = 5;
    quote!(
        #data
    ).into()
}

But if I try to do the same returning an array or vec I get an error:

#[proc_macro]
pub fn pre_calc_values(_input: TokenStream) -> TokenStream {
    let data = [1,2,3];
    // example data
    // generate actual data into the array here at compile time
    quote!(
        #data
    ).into()
}

the trait bound [{integer}; 3]: ToTokens is not satisfied
the following other types implement trait ToTokens

the trait bound Vec<{integer}>: ToTokens is not satisfied
the following other types implement trait ToTokens

Is this possible? If so, what am I doing wrong?

Thanks for your time!

Quote the array!

let data = quote! { [1,2,3] };

Thanks, I can see that works if I'm specifying the array as a constant. But I can't get that to work when I am creating it dynamically.

#[proc_macro]
pub fn make_squares_input: TokenStream) -> TokenStream {
    let mut squares:Vec<u8> = vec!();
    
    for n in 1..10 {
        squares.push(n * n);
    }

    let data = quote!(
        squares
    );

    quote!(
        #data
    ).into()
}

When I use that it says squares is not defined, which make sense.

But I realised I just need to output actual rust code as if I was typing it, so this works:

#[proc_macro]
pub fn make_squares_input: TokenStream) -> TokenStream {
    let mut squares:Vec<u8> = vec!();
    
    for n in 1..10 {
        squares.push(n * n);
    }

    let mut token_string = String::new();
    token_string += "vec!(";
    
    for s in squares {
        token_string += &format!("{},", s);
    }
    token_string += ")";

    TokenStream::from_str(&token_string).unwrap()
}

I suspect there is a way to quote!(...) this somehow and avoid having to write the loop and build a string. I think my actual question then is, how do I quote the contents of a vec, rather than the vec itself?

You aren't aware of what quote! does, are you? It transforms its input to a token stream that literally represents what is written inside it. quote!(squares) literally results in the single identifier squares being emitted.

If you want to include dynamically-formatted subtrees in the emitted token tree, you need to splice them in explicitly using the # syntax. That's documented in the documentation of quote, you should read it.

1 Like

This is what quote! does... It builds "strings".

The macro takes a TokenStream as input, replaces all #interpolations iff the type implements the ToTokens traits, and outputs a new TokenStream.

Like this:

quote! {
    vec![
        #(#squares,)*
    ]
}

This is a confusing question. The quote macro doesn't know anything about your vector. It only knows about tokens and types that implement ToTokens. Arrays do not implement the macro, so you cannot interpolate them with the #syntax. Vectors do implement the trait, as long as the contained type also does. Most primitives have a default implementation, including the integer types.

The documentation describes the #interpolation conventions, including iteration, which is probably what answers your question: quote in quote - Rust

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.