es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

¿Es posible eliminar campos o reemplazar un campo existente en una estructura Rust con macros?

Estoy intentando escribir una macro de atributo para un proc-macro que me permita eliminar un campo de una estructura o reemplazar completamente su firma.

Algo similar a esto:

#[derive(my_macro)]
struct OurStruct{
       a: i32,
       #[my_macro(remove)]
       field_to_remove: i32
}

Lo cual debería resultar en:

struct OurStruct{
       a: i32,
}

Sé cómo escribir macros para agregar a la parte “impl” de la estructura, pero hasta ahora tengo problemas para encontrar algún ejemplo sobre cómo modificar la parte del código marcada por una atributo-macro.

¿Es eso posible? Si es así, ¿puede proporcionar un ejemplo, por favor?

Tags:  , ,

Answer

  1. Avatar for davy.ai

    Sí, es posible escribir un macro de atributo para un proc-macro que elimina o reemplaza un campo de una estructura. Para lograr esto, puedes usar una combinación de atributos de proc-macro y funciones de manipulación de estructuras.

    Aquí tienes un ejemplo de implementación de dicho macro de atributo:

    use proc_macro::TokenStream;
    use quote::quote;
    use syn::{parse_macro_input, Data, DeriveInput, Fields};
    
    #[proc_macro_attribute]
    pub fn my_macro(args: TokenStream, input: TokenStream) -> TokenStream {
        // Parse los tokens de entrada en un árbol de sintaxis
        let input = parse_macro_input!(input as DeriveInput);
        let mut output = quote!();
    
        // Comprueba si se pasa el argumento "remove" al macro
        let remove_field = args.to_string().contains("\"remove\"");
    
        // Itera sobre los campos de la estructura
        let fields = match input.data {
            Data::Struct(ref data) => match data.fields {
                Fields::Named(ref fields) => fields.named.iter().map(|f| {
                    let name = &f.ident;
                    let ty = &f.ty;
                    quote! {
                        #name : #ty
                    }
                }).collect(),
                _ => panic!("Solo se admiten campos con nombres")
            },
            _ => panic!("Solo se admiten estructuras")
        };
    
        // Genera la estructura de salida con los campos deseados
        output.extend(quote! {
            struct #input.ident {
                #(#fields),*
            }
        });
    
        // Devuelve el flujo de tokens final
        TokenStream::from(output)
    }
    

    Con este macro, puedes anotar campos con el atributo #[my_macro(remove)] para eliminarlos de la estructura final:

    #[derive(my_macro)]
    struct OurStruct {
           a: i32,
           #[my_macro(remove)]
           field_to_remove: i32
    }
    

    Esto resulta en la siguiente estructura:

    struct OurStruct {
        a: i32,
    }
    

    De manera similar, puedes proporcionar un campo de reemplazo pasando su nombre como argumento de cadena al macro:

    #[derive(my_macro)]
    struct OurStruct {
        a: i32,
        #[my_macro(replace = "new_field: i64")]
        field_to_replace: i32,
    }
    

    Esto resulta en la siguiente estructura:

    struct OurStruct {
        a: i32,
        new_field: i64,
    }
    

    Ten en cuenta que este ejemplo solo admite reemplazar un campo con un único campo nuevo. Si necesitas reemplazar un campo con varios campos o con una estructura anidada, deberías modificar el código en consecuencia.

Comments are closed.