如何让我的自定义派生宏接受特征通用参数?
How do I make my custom derive macro accept trait generic parameters?
我正在尝试为我的特征实现自定义派生宏,它们确实有效!
但是我有一个小问题。我似乎找不到将通用参数包含到特征中的方法。
具体来说,我想做这样的事情:#[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
}
...
}
如您所见,我包括 'a、V 和 E fixed在引用块中,而不是我想要实现的东西,它能够灵活地派生出我想要的泛型类型的特征。
我想要的是类似于此的东西:
#[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
} ...
我认为不可能完全使用您在 post (#[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
,您可以从中提取必要的参数。例如,如果你的特征有一个生命周期和两个类型参数,你的解析逻辑可能看起来像这样:
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`
}
我正在尝试为我的特征实现自定义派生宏,它们确实有效!
但是我有一个小问题。我似乎找不到将通用参数包含到特征中的方法。
具体来说,我想做这样的事情:#[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
}
...
}
如您所见,我包括 'a、V 和 E fixed在引用块中,而不是我想要实现的东西,它能够灵活地派生出我想要的泛型类型的特征。
我想要的是类似于此的东西:
#[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
} ...
我认为不可能完全使用您在 post (#[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
,您可以从中提取必要的参数。例如,如果你的特征有一个生命周期和两个类型参数,你的解析逻辑可能看起来像这样:
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`
}