Escribir un macro bitflags: introducción a los macros procedimentales de Rust

Fuentes: Writing a Bitflags Macro: An Introduction to Rust Procedural Macros
Imagen generada por IA con el prompt: Rust code editor showing macro expansion with token trees, syntax highlighting in dark theme, abstract code generation visualization
Imagen generada con IA

Este capítulo explora los macros procedimentales de Rust mediante la construcción de un macro bitflags desde cero. Comienza distinguiendo los macros de las funciones ordinarias: aunque ambos mapean entradas a salidas, los macros operan sobre el código fuente en lugar de sobre variables en tiempo de ejecución, transformando un TokenStream en otro durante la compilación. Un TokenStream es la unidad de tokenización básica del sistema proc_macro de Rust y contiene una secuencia de nodos TokenTree —Group, Ident, Punct y Literal— que representan la estructura del código fuente.

El capítulo explica que los macros, igual que las funciones, son mappings, pero en lugar de traducir a un lenguaje destino como ASM, traducen código Rust a código Rust. Por ejemplo, un macro_rules! square que duplica una expresión se expande en una multiplicación literal en el punto de llamada. Esta transformación a nivel de código fuente permite capacidades que las funciones ordinarias no tienen, como inyectar construcciones de control de flujo como break en el ámbito que las contiene. Una función que devuelve Option no puede salir directamente de un bucle externo, pero un macro que se expanda a expresiones match con break sí puede.

El texto clasifica luego los tipos de macros en Rust, empezando por los macros declarativos definidos con macro_rules!. Estos «macros por ejemplo» usan metavariables prefijadas con $ (como $e:expr o $num:expr) como marcadores que coinciden con fragmentos de sintaxis Rust y se sustituyen durante la expansión. Los tipos de metavariable más comunes son expr (expresiones), ident (identificadores) y ty (tipos). El capítulo conecta estos patrones con la librería syn, que analiza la sintaxis de Rust en representaciones estructuradas, anticipando la transición hacia los macros procedimentales.

La explicación de cómo se ejecutan los macros destaca la evaluación en tiempo de compilación: una función proc-macro recibe un TokenStream como entrada y debe devolver otro TokenStream como salida, y el compilador de Rust realiza la sustitución antes de generar el código. El capítulo sirve como preparación para implementar un proc-macro bitflags propio y aporta los fundamentos conceptuales necesarios para manipular árboles sintácticos de forma programática en lugar de mediante simple coincidencia de patrones.