如何让我的自定义派生宏接受特征通用参数?

5 rust rust-proc-macros

我正在尝试为我的特征实现自定义派生宏,它们确实有效!

但是我有一个小问题。我似乎找不到一种方法来将通用参数包含到特征中。

具体来说,我想做这样的事情: #[derive(MyCustomDerive<'a, B, C>)]

相反,现在我正在对泛型进行硬编码,如下所示:

let gen = quote! {
        impl #impl_generics Graph<'a, V, E> for #name #ty_generics #where_clause {
            fn Map(&self) -> &MAP<V, E> {
                &self.map
            }
            ...
}
Run Code Online (Sandbox Code Playgroud)

如您所见,我在引用块中包含固定的'aVE,而不是我想要实现的目标,即能够使用我想要的泛型类型灵活地派生特征。

我想要的是类似于这样的东西:

#[derive(MyCustomDerive<'a, B, C>)]

导致与此相同的事情

let gen = quote! {
        impl #impl_generics Graph<'a, B, C> for #name #ty_generics #where_clause {
            fn Map(&self) -> &MAP<B, C> {
                &self.map
            }
            ...
}
Run Code Online (Sandbox Code Playgroud)

这将允许我保留(当然如果有必要)V 和 E 用于其他事情,并且在我看来使代码更易于控制。感谢您的帮助!

更新 1:这就是我的派生函数的样子

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
           } ...
Run Code Online (Sandbox Code Playgroud)

Alp*_*der 8

我认为不可能完全使用您在帖子 ( #[derive(MyCustomDerive<'a, B, C>)]) 中描述的语法。但是,请考虑以下使用附加自定义属性的语法:

#[derive(MyTrait)]
#[my_trait('a, B, C)]
struct MyStruct {
    // ...
}
Run Code Online (Sandbox Code Playgroud)

要允许使用该my_trait属性,您必须attributes向您的proc_macro_derive属性添加一个部分。

#[proc_macro_derive(MyTrait, attributes(my_trait))]
pub fn derive_my_trait(input: TokenStream) -> TokenStream {
    // ...
}
Run Code Online (Sandbox Code Playgroud)

有关解析属性本身的帮助,请查看syn::Attribute. 该tokens字段是TokenStream您可以从中提取必要参数的字段。例如,如果您的 trait 有一个生命周期和两个类型参数,您的解析逻辑可能如下所示:

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`
}
Run Code Online (Sandbox Code Playgroud)