Reputation: 1417
I have a struct and a trait:
struct Foo {
i: i32,
}
trait FooTrait {
fn foo(&self);
}
I want to create a derive macro for the struct which generates the impl:
impl FooTrait for Foo {
fn foo(&self) {
println!("generated code: {}", self.i);
}
}
When I attempted to achieve this, I'm facing the blocker that my derive macro doesn't seem to have a way to know the token stream of FooTrait
, where I need to iterate through the methods of FooTrait
, and generate the implementation for each trait method based on Foo
!
How can I achieve that?
This question is not about how to use quote!
to quote the trait impl and spell out foo
directly - the hard part is that I want to procedurally iterate through the methods of FooTrait
, and generate some boilerplate code for each trait method.
Upvotes: 0
Views: 1655
Reputation: 170815
A possible workaround: instead of defining FooTrait
directly, encode the information you need about it in some structure, store it in a const
and access it both from the derive macro and another macro generating the definition of FooTrait
.
EDIT: if you want exactly "the token stream of FooTrait
", you don't need the const
(this is just a skeleton and not tested):
The macros crate:
#[proc_macro]
pub fn foo_trait_definition(_input: TokenStream) -> TokenStream {
TokenStream::from(quote!(
trait FooTrait {
... // actual definition goes here
}
))
}
#[proc_macro_derive(FooTrait)]
pub fn foo_trait_derivation(input: TokenStream) -> TokenStream {
let foo_trait_tokens: TokenStream = foo_trait_definition(TokenStream::from(quote!()));
// you can parse foo_trait_tokens and use them just like input
TokenStream::from(quote!(
impl foo_mod::FooTrait for #name {
...
}
))
}
The trait crate:
mod foo_mod {
foo_trait_definition!();
}
and
#[derive(FooTrait)]
struct Foo {
i: i32,
}
But I expect that in most cases the macros crate can look like this instead of parsing foo_trait_tokens
:
// lists method names here, if they are all (&self)
// but probably you want something more complex
const foo_trait_data = ["foo"];
#[proc_macro]
pub fn foo_trait_definition(_input: TokenStream) -> TokenStream {
// use foo_trait_data here
TokenStream::from(quote!(
trait FooTrait {
... // actual definition goes here
}
))
}
#[proc_macro_derive(FooTrait)]
pub fn foo_trait_derivation(input: TokenStream) -> TokenStream {
// use foo_trait_data here too
TokenStream::from(quote!(
impl foo_mod::FooTrait for #name {
...
}
))
}
Upvotes: 0
Reputation: 431609
You don't.
By construction, macros are only provided with the source code of the item they are attached to. If you attach the macro to the struct, you don't get to see the trait.
Similar derive macros bake in the knowledge about the trait being implemented. They may also parse custom attributes that allow the user to configure details about the generated code. Your macro will likely do the same.
See also:
Upvotes: 2