El concepto detrás de defer en Zig es muy sencillo: una instrucción
precedida por esta palabra siempre será ejecutada al final del bloque de
contexto actual.
¿Por qué alguien querría hacer eso? Lo veremos a continuación.
Caso de uso
Limpieza de un asignador de memoria
Recordemos este pasaje en nuestra entrada sobre un Arraylist
en Zig:
El
ArrayListacepta un Tipo(T),i32en nuestro ejemplo, como argumento y la gestión de memoria ocurre de forma automática durante el ciclo de vida del mismo. Sin embargo, la inicialización y destrucción del vector debe hacerse explicitamente:
const allocator = std.heap.page_allocator;
var list = ArrayList(i32).init(allocator);
defer list.deinit();
La variable
allocatorguarda el asignador de memoriapage_allocatorque provee la biblioteca estándar de Zig. La variablelistinicializa elArraylist. Por último, hacemos la destrucción delistusando el métododeinit()acompañado de la palabradefer.
En este escenario, posterior a la instrucción de:
defer list.deinit();
Tenemos algunas otras instrucciones que el programa va a ejecutar:
try list.append(100)
std.debug.print(
"El Elemento en el indice 0 es: {}",
.{list.items[0]},
);
Todo lo anterior se encuentra en el ámbito de la funcion:
pub fn main() {
}
Siguiendo esa lógica, muchas personas pensarían que la instrucción de:
std.debug.print(
"El Elemento en el indice 0 es: {}",
.{list.items[0]},
);
Sería lo último en ejecutarse.
Equivocados. En realidad lo último que se
ejecuta en el contexto de la función main() es la instrucción de:
defer list.deinit();
Y tiene sentido si revisamos la traducción literal al Español del término
defer: significa “posponer” o “aplazar”. Lo que estamos haciendo es
basicamente aplazando una instrucción para el final del bloque {} de
contexto actual (main()).
Entonces, ¿Por qué no simplemente dar la instruccion al final del bloque
de contexto? Bueno, la razón es porque como seres humanos tendemos a
olvidar fácilmente algunas cosas o pasar detalles por alto. Por lo cual,
declarar defer justo después de hacer una asignación manual de memoria es
mucho más sencillo que recordar hacerlo al final de cada bloque de
contexto.
Es como decirle al compilador: “hey, no se te olvide hacer limpieza y eliminar este asignador de memoria”.
A medida que estudiamos Zig, veremos si es posible que el compilador
retorne error si olvidamos dar la instrucción de defer o no. Lo
descubriremos en una posible futura entrega.
Otro caso de uso relativamente común para defer es en la manipulación de
archivos. En ese caso, se hace para no olvidar cerrar un archivo que se ha
abierto usando Zig.
Espero que haya quedado un poco más claro el funcionamiento de defer en
Zig.