Variables en Zig
Las variables constituyen el elemento fundamental de cualquier lenguaje de programación, siendo los espacios de memoria donde almacenamos valores durante la ejecución de nuestro programa. Zig presenta un enfoque claro y sistemático respecto a las variables, priorizando la seguridad y el control explícito sobre la memoria.
Variables mutables e inmutables
En Zig existen dos tipos principales de variables:
- Variables inmutables (
const
): Una vez asignadas, no pueden cambiar su valor. - Variables mutables (
var
): Pueden modificar su valor después de la asignación inicial.
const constante: u32 = 1024; // Variable inmutable de tipo entero sin signo de 32 bits
var variable: i32 = -5; // Variable mutable de tipo entero con signo de 32 bits
// constante = 2048; // ¡Error de compilación! No se puede modificar una constante
variable = 10; // Correcto, las variables mutables pueden cambiar
Como práctica recomendada, es preferible utilizar const
siempre que sea posible. Esto no solo comunica la intención de que el valor no debería cambiar, sino que también permite al compilador realizar ciertas optimizaciones y evita errores accidentales de modificación.
Declaración de tipos
Zig ofrece flexibilidad en la declaración de los tipos de las variables:
Declaración explícita
El tipo se especifica directamente en la declaración mediante la sintaxis : tipo
después del identificador:
const edad: u8 = 30; // Entero sin signo de 8 bits
const altura: f32 = 1.75; // Punto flotante de 32 bits
const nombre: []const u8 = "Ana"; // Slice de caracteres inmutables
Declaración implícita (inferencia de tipos)
El compilador puede deducir el tipo basándose en el valor asignado:
const edad = 30; // Se infiere como entero (comptime_int)
const altura = 1.75; // Se infiere como flotante (comptime_float)
const nombre = "Ana"; // Se infiere como *const [3:0]u8 (string literal)
La inferencia de tipos en Zig es conservadora y precisa. En los ejemplos anteriores, los números son tratados como valores de tiempo de compilación (comptime_int
y comptime_float
) hasta que se asignan a una variable de tipo específico.
Inicialización y undefined
Una característica distintiva de Zig es que toda variable debe tener un valor asignado al ser declarada. Esto refuerza la seguridad del código al evitar el uso de valores no inicializados.
Si no es posible proporcionar un valor significativo durante la declaración, se puede usar el valor especial undefined
:
var buffer: [100]u8 = undefined; // Se reserva espacio para 100 bytes sin inicializarlos
const estructura: MiEstructura = undefined; // Se reserva espacio para la estructura
// Posteriormente se asignan valores reales
buffer[0] = 65; // 'A' en ASCII
Es importante entender que undefined
no es "un valor especial que representa la ausencia de valor". En su lugar, significa "no me importa qué haya en esta memoria ahora mismo, voy a escribir algo antes de leerla". Usar memoria marcada como undefined
sin inicializarla primero puede llevar a comportamientos impredecibles.
Coerción de tipos
La coerción de tipos en Zig permite convertir valores entre tipos compatibles. Existen dos mecanismos principales:
Coerción implícita
Zig permite coerciones implícitas cuando son seguras y no hay pérdida de información:
const pequeño: u8 = 5; // Entero de 8 bits
const grande: u16 = pequeño; // Coerción implícita a entero de 16 bits
Esto funciona porque un u8
siempre puede representarse como un u16
sin perder información.
Coerción explícita con @as
Para casos donde se requiere una conversión explícita, se utiliza la función @as
:
const grande: u16 = 1000;
// const pequeño: u8 = grande; // ¡Error! Un u16 no cabe necesariamente en un u8
const pequeño: u8 = @as(u8, 100); // Correcto, el valor 100 es compatible con u8
La función @as
intenta convertir el valor al tipo especificado, pero fallará en tiempo de compilación o producirá un error en tiempo de ejecución si la conversión causa la pérdida de información.
Ámbito de las variables
Las variables en Zig tienen un ámbito (scope) determinado por el bloque en el que se declaran:
{
const x: i32 = 10; // x solo existe dentro de este bloque
{
const y: i32 = 20; // y solo existe dentro de este sub-bloque
// Aquí podemos acceder tanto a x como a y
}
// Aquí solo podemos acceder a x, y ya no existe
}
// Aquí ni x ni y existen
Además, Zig no permite el sombreado (shadowing) de variables, es decir, no se puede declarar una nueva variable con el mismo nombre dentro del mismo ámbito o un ámbito anidado:
const x: i32 = 10;
{
// const x: i32 = 20; // ¡Error! No se permite sombrear la variable x
}
Variables a nivel de contenedor
Las variables declaradas en el nivel superior de un archivo o dentro de una estructura se llaman "variables a nivel de contenedor". Estas variables tienen un comportamiento especial:
const PI: f32 = 3.14159; // Variable global inmutable
var contador: u32 = 0; // Variable global mutable
pub fn incrementarContador() void {
contador += 1; // Modificamos la variable global
}
Las variables a nivel de contenedor siguen las mismas reglas de mutabilidad que las variables locales, pero tienen un tiempo de vida que dura toda la ejecución del programa.
Conclusión
El sistema de variables de Zig combina la simplicidad y la seguridad a través de reglas claras y coherentes. La distinción entre const
y var
promueve un estilo de programación que minimiza el estado mutable, mientras que la obligación de inicializar todas las variables ayuda a prevenir errores comunes relacionados con valores no inicializados.
La coerción de tipos en Zig es conservadora y segura, permitiendo conversiones solo cuando no hay riesgo de pérdida de información o comportamientos inesperados. Esta filosofía de diseño refleja el enfoque general de Zig: proporcionar control explícito sobre el comportamiento del código, evitando sorpresas y optimizando la claridad y la seguridad.