如何使我的自定义派生宏接受特质泛型参数?

13

我正在尝试为我的特质实现自定义派生宏,它们实际上起作用了!

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

具体来说,我想做这样的事情:#[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
            }
            ...
}

正如您所看到的,我在引用块中固定了'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
            }
            ...
}
这将使我能够为其他事情保留(当然必要时)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
           } ...

你使用 syn crate 吗? - Boiethios
@FrenchBoiethios 是的,是的,我会。 - user7924331
1个回答

22

我不相信你在帖子中描述的语法可以完全使用 (#[derive(MyCustomDerive<'a, B, C>)])。但是,考虑使用另一个自定义属性的以下语法:

#[derive(MyTrait)]
#[my_trait('a, B, C)]
struct MyStruct {
    // ...
}
为了允许使用my_trait属性,您需要在proc_macro_derive属性中添加一个attributes部分。
#[proc_macro_derive(MyTrait, attributes(my_trait))]
pub fn derive_my_trait(input: TokenStream) -> TokenStream {
    // ...
}

如果需要解析属性本身,请查看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`
}

非常感谢您的回答!我已经为此苦恼了一整天了 :) 您真是我的救星!关于属性宏,我找不到太多文档,这太棒了! - user7924331
1
@StefanChircop 很高兴我能帮到你!过程宏是一种相对较新的语言特性,因此目前文档相当稀少。浏览其他过程宏包的源代码是学习如何做事情的好方法。(当然,在这里提问也是一个好方法!) - AlphaModder
如果我可以问一下,使用您的实现,我会得到以下错误: 14 | #[GraphStruct<'a, V, E, map, inc, out, sen, rec, nodes, edges>] | ^ expected one of (, ::, =, [, ], or { here如果我在属性中将<>替换为(),则会得到以下结果: Invalid GraphStruct attribute!: Error("expected <") 尝试解析参数时出错。 然而,'Token!'不允许与'('一起使用,所以我很困惑。 - user7924331
1
@StefanChircop 哦,显然 Rust 在属性中允许出现的内容仍有一些限制。由于您似乎不介意使用括号,我已更新我的答案以显示如何解析它们。 - AlphaModder
非常感谢您的耐心等待 :) - user7924331
@StefanChircop 没问题,是我自己的错,没有确认尖括号是否有效。 - AlphaModder

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接