Как сделать так, чтобы мой собственный производный макрос принимал общие параметры черты?
Я пытаюсь реализовать пользовательские макросы для своих черт, и они действительно работают!
Однако у меня есть небольшая проблема. Я не могу найти способ включить общие параметры в черту.
В частности, я хочу сделать что-то вроде этого: #[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, зафиксированные в блоке кавычек, вместо того, что я хочу достичь, что позволяет гибко выводить черту с общими типами, которые я хочу.
Что я хотел бы, это что-то похожее на это:
#[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
} ...
1 ответ
Я не верю, что можно использовать именно тот синтаксис, который вы описали в своем посте (#[derive(MyCustomDerive<'a, B, C>)]
). Однако рассмотрим следующий синтаксис, в котором вместо этого использовался дополнительный настраиваемый атрибут:
#[derive(MyTrait)]
#[my_trait('a, B, C)]
struct MyStruct {
// ...
}
Чтобы позволить my_trait
атрибут будет использоваться, вам нужно будет добавить attributes
раздел к вашему proc_macro_derive
приписывать.
#[proc_macro_derive(MyTrait, attributes(my_trait))]
pub fn derive_my_trait(input: TokenStream) -> TokenStream {
// ...
}
Для помощи в разборе самого атрибута, посмотрите на syn::Attribute
, tts
поле является 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 && attr.path.segments[0].ident == "my_trait"
).nth(0).expect("my_trait attribute required for deriving MyTrait!");
let parameters: MyParams = syn::parse2(attribute.tts.clone()).expect("Invalid my_trait attribute!");
// ... do stuff with `parameters`
}