Как сделать так, чтобы мой собственный производный макрос принимал общие параметры черты? - PullRequest
2 голосов
/ 17 мая 2019

Я пытаюсь реализовать пользовательские макросы для своих черт, и они действительно работают!

Однако у меня небольшая проблема. Я не могу найти способ включить универсальные параметры в черту.

В частности, я хочу сделать что-то вроде этого: #[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 Ответ

1 голос
/ 18 мая 2019

Я не верю, что можно использовать именно тот синтаксис, который вы описали в своем посте (#[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`
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...