Algoritmos y Estructuras de Datos Herramientas Lenguaje de programación
!Prog C/C++ Rust
Linux Matemáticas
Mates Discretas
Programación Orientada a Objetos Sistemas Operativos

Macros


[words: 712] [reading time: 4min] [size: 21532 bytes]

rocket doc

Proceso de compilación de Rust

La primera etapa es la tokenización, donde se convierte el texto en símbolos (unidades indivisibles del lenguaje, son como las palabras de Rust). Algunos ejemplos de tokens:

Notas:

  • self es un identificador y una palabra clave (normalmente este último).
  • yield y macro no son palabras clave como tal, pero el compilador las interpreta así.
  • <- se eliminó de la gramática pero no del lexer.
  • :: no son dos :, sino que se trata de un símbolo totalmente diferente.

En este punto, algunos lenguajes procesan aquí los macros, por eso esto funciona:

1#include <stdio.h>
2
3#define SUB void
4#define BEGIN {
5#define END }
6
7SUB main() BEGIN
8    printf("Pesadillas!\n");
9END

La siguiente fase es el Parser, donde los tokens se cambian por el Árbol Sintáctico Abstracto (AST) que contiene la estructura del programa entero.

Una vez hecho eso, es cuando los macros se procesan.

Macros

Las macros permiten escribir código que escriba otro código, lo que se llama metaprogramming, es decir, definiremos una especie de función que se ejecutará y como resultado modificará nuestro archivo de código fuente.

Las macros son similares a las funciones, pero sin el coste extra en la hora de ejecución, aunque se aumenta el coste de compilación.

Nota: Los macros en Rust son muy diferentes a otros lenguajes como por ejemplo C, que solo es substitución de texto.

Metaprogramming se usa para reducir la cantidad de código que tienes que escribir y mantener, que es el mismo motivo de las funciones, pero los macros ofrecen otras posibilidades.

Tipos de macros

Esto del TokenStream es como funciona el compilado de Rust. En lugar de cambiar directamente el contenido del archivo, se modifican los símbolos que el compilador ha entendió.

Macros declarativos

Se crean con macro_rules!. Son algo menos poderosos, pero son bastante sencillos para acortar código y eliminar partes duplicadas. Uno de los más comunes de este tipo es println!.

 1// Indica que este macro se puede usar en cualquier lugar dentro del crate.
 2// Sin esta anotación, al macro no podrá entrar en scope
 3
 4#[macro_export]
 5
 6// Aquí no es necesario añadir la `!`
 7macro_rules! <macro_name> {
 8    // Esta sintáxis es similar a un match
 9    ($a: expr, $b: expr) => {
10        { // Añadimos ´{}´ extra en caso de que creemos una variable.
11          // Al terminarse el macro, el compilador se encontrará con `}` y
12          // borrará el valor, por lo que no tendremos errores inesperados
13            // los valores $a y $b se sustituiran y luego se sumarán
14            $a + $b
15        }
16    };
17
18    // Se pueden añadir varios brazos para usar diferentes argumentos
19    // en el mismo macro
20    ($a: expr) => {
21        {
22            $a
23        }
24    };
25}

También podemos pasarle a un macro un número indeterminado de argumentos, * se usa para 0 o más y + para 0 o 1. La parte que se repite debe ir entre $() luego una coma y */+.

 1#[macro_export]
 2macro_rules! vec {
 3    ( $( $x:expr ), * ) => {
 4        {
 5            let mut temp_vec = Vec::new();
 6            $(
 7                temp_vec.push($x);
 8            )*
 9            temp_vec
10        }
11    };
12}

Estos son los símbolos que representan los valores precedidos de $:

Macros procedurales

Existen tres tipos:

Anterior: Listas y Iteradores Volver a Rust Siguiente: Módulos