I am trying to implement custom derive macros for my traits, and they actually work!
However I have a slight problem. I can't seem to find a way to include generic parameters to the trait.
Specifically, I want to do something like this : #[derive(MyCustomDerive<'a, B, C>)]
Instead, right now I am hard-coding the generics, like so :
let gen = quote! {
impl #impl_generics Graph<'a, V, E> for #name #ty_generics #where_clause {
fn Map(&self) -> &MAP<V, E> {
&self.map
}
...
}
As you can see, I am including 'a, V and E fixed within the quote block, instead of something I want to achieve, which is being able to flexibly derive the trait with the generic types I want.
What I would like is something akin to this :
#[derive(MyCustomDerive<'a, B, C>)]
to result in something equivalent to this
let gen = quote! {
impl #impl_generics Graph<'a, B, C> for #name #ty_generics #where_clause {
fn Map(&self) -> &MAP<B, C> {
&self.map
}
...
}
This would allow me to reserve (of course if necessary) V and E for other things and in my opinion make code more controllable. Thank you for your help!
Update 1 : This is how my derive function looks
pub fn derive(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
let generics = &ast.generics;
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let gen = quote! {
impl #impl_generics Graph<'a, V, E> for #name #ty_generics #where_clause {
fn Map(&self) -> &MAP<V, E> {
&self.map
} ...
I don't believe it's possible to use exactly the syntax you describe in your post (#[derive(MyCustomDerive<'a, B, C>)]
). However, consider the following syntax that used an additional custom attribute instead:
#[derive(MyTrait)]
#[my_trait('a, B, C)]
struct MyStruct {
// ...
}
To allow the my_trait
attribute to be used, you'll have to add an attributes
section to your proc_macro_derive
attribute.
#[proc_macro_derive(MyTrait, attributes(my_trait))]
pub fn derive_my_trait(input: TokenStream) -> TokenStream {
// ...
}
For help with parsing the attribute itself, take a look at syn::Attribute
. The tokens
field is a TokenStream
from which you could extract the necessary parameters. For example, if your trait has one lifetime and two type parameters, your parsing logic might look something like this:
struct MyParams(syn::Lifetime, syn::Ident, syn::Ident);
impl syn::Parse for MyParams {
fn parse(input: syn::ParseStream) -> Result<Self> {
let content;
syn::parenthesized!(content in input);
let lifetime = content.parse()?;
content.parse::<Token![,]>()?;
let type1 = content.parse()?;
content.parse::<Token![,]>()?;
let type2 = content.parse()?;
Ok(MyParams(lifetime, type1, type2))
}
}
pub fn derive(ast: &syn::DeriveInput) -> TokenStream {
let attribute = ast.attrs.iter().filter(
|a| a.path.segments.len() == 1 && a.path.segments[0].ident == "my_trait"
).nth(0).expect("my_trait attribute required for deriving MyTrait!");
let parameters: MyParams = syn::parse2(attribute.tokens.clone()).expect("Invalid my_trait attribute!");
// ... do stuff with `parameters`
}