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
ArrayList
acepta un Tipo(T)
,i32
en 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
allocator
guarda el asignador de memoriapage_allocator
que provee la biblioteca estándar de Zig. La variablelist
inicializa elArraylist
. Por último, hacemos la destrucción delist
usando 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.