While this seems like an easy request, there is actually a lot to unroll here.
Most importantly, it is crucial to understand that procedural macros only return tokens (i.e. Rust code). To put it bluntly: the Rust compiler executes your procedural macro, takes the resulting tokens and pastes them in the users code where your procedural macro invocation was. You can think of procedural macros as a pre-processing step that takes your Rust code, transforms it and spits out another .rs
file. That file is then fed to the compiler.
In order to "return a value of Foo
" you have to return a TokenStream
that represents an expression which evaluates to Foo
. For example:
#[proc_macro]
pub fn create_foo(_: TokenStream) -> TokenStream {
quote! { Foo { data: vec![1, 2, 3] } }
}
In the user's crate:
let a: Foo = create_foo!();
Which would expand to:
let a: Foo = Foo { data: vec![1, 2, 3] };
The data: vec![1, 2, 3]
part could be generated dynamically by the procedural macro. If your Foo
instance is very large, the code creating that instance is probably very large as well. This means that compile times might increase because the Rust compiler has to parse and check this huge expression.
So you can't return the value directly? No. You might think that you could do it with unsafe
code. For example, emit a big const DATA: &[u8] = ...;
and mem::transmute
it to Foo
, but you can't for several reasons:
- The procedural macro and the user's crate might not run on the same platform (CPU, OS, ...) which all might influence how
Foo
is represented in memory. The same Foo
instance might be represented differently in memory for your procedural macro and your user crate, so you can't transmute
.
- If
Foo
contains heap allocated structures (Vec
), you can't do it anyway.
If you must generate the value in your procedural macro, then there is only one solution to get it to the user, but this is not optimal. Alternatively, maybe calculating it at runtime once isn't that bad.