El lenguaje de programación Zig ofrece un sistema de tipos que combina la rigidez del tipado estático con la flexibilidad del tipado dinámico en tiempo de compilación. Este sistema permite que los programas sean seguros, eficientes y expresivos, al tiempo que facilita la depuración y la optimización del código.
Conceptos Fundamentales del Sistema de Tipos Zig
- Tipado estático: Todos los tipos se verifican en tiempo de compilación.
- Tipado fuerte: No se permiten conversiones implícitas entre tipos incompatibles.
- Tipado con inferencia limitada: Zig infiere tipos en ciertos contextos pero siempre se puede especificar explícitamente.
- Tipado en tiempo de compilación (comptime): Muchas operaciones sobre tipos se realizan en el compilador.
- Tipado genérico: Se utilizan plantillas de tipos que se especializan en tiempo de compilación.
Tipos de Datos Primarios
- Enteros sin signo:
u8,u16,u32,u64,u128 - Enteros con signo:
i8,i16,i32,i64,i128 - Coma flotante:
f16,f32,f64,f128 - Booleano:
bool - Caracteres:
u8(ASCII),u32(Unicode scalar)
Ejemplo: Definición y Uso de Tipos Primitivos
// Definición de variables con tipos primitivos
const a: u32 = 42; // a es un entero sin signo de 32 bits
const b: f64 = 3.14159; // b es un número de punto flotante de doble precisión
const c: bool = true; // c es un valor booleano
const d: u8 = 255; // d es el mayor valor posible de u8 (8 bits)
// Imprimimos los valores para comprobar
std.debug.print("a = {}\n", .{a});
std.debug.print("b = {}\n", .{b});
std.debug.print("c = {}\n", .{c});
std.debug.print("d = {}\n", .{d});
En este fragmento:
constindica una variable inmutable.- Los tipos se especifican después del nombre con
:. - Las funciones de impresión utilizan el motor de formato de Zig, donde
{}es el marcador de posición. - El punto
.después del primer argumento deprintindica que el segundo argumento es una tupla de valores a formatear.
Tipos de Datos Estructurados
Zig permite crear estructuras y uniones, así como enumeraciones que son fundamentales para la organización de datos complejos.
Estructuras
const Point = struct {
x: f64,
y: f64,
};
var p: Point = .{ .x = 1.0, .y = 2.0 };
// Acceso a campos
std.debug.print("Point p: ({}, {})\n", .{p.x, p.y});
Explicación:
- La palabra clave
structdefine un nuevo tipo compuesto. - Los campos se enumeran con
nombre: tipo. - La inicialización con
.{ .campo = valor }es obligatoria y explícita. - El operador
.antes dexyyaccede a los campos de la estructura.
Uniones
const Value = union(enum) {
int: i32,
float: f32,
bool: bool,
// Método para convertir a string
fn toString(self: Value) []const u8 {
return switch (self) {
.int => "int",
.float => "float",
.bool => "bool",
};
}
};
var v: Value = .{ .int = 7 };
std.debug.print("Value type: {}\n", .{v.toString()});
Notas:
- Una
union(enum)combina la semántica de una unión con un discriminador enumerado. - La etiqueta
selfes la referencia al propio valor. - El
switchpermite seleccionar el caso activo y ejecutar código correspondiente.
Enumeraciones
const Color = enum(u8) {
Red = 1,
Green,
Blue,
};
var c: Color = .Green; // Green toma el valor 2 automáticamente
std.debug.print("Color Green = {}\n", .{@intFromEnum(c)});
Detalles:
- El tipo subyacente
u8determina el tamaño del enumerador. - Los valores sucesivos se incrementan automáticamente si no se especifican explícitamente.
- La función
@intFromEnumconvierte el valor de enum a su representación entera.
Tipos de Puntero y Referencia
Zig diferencia claramente entre punteros que pueden ser nulos y punteros no nulos. Además, permite punteros a tipos genéricos y punteros constantes.
Puntero Nulo y No Nulo
var num: i32 = 10;
// Puntero no nulo (constante)
var ptr: *const i32 = #
// Puntero nulo (opcional)
var opt_ptr: ?*i32 = null;
// Asignamos una dirección válida
opt_ptr = #
// Acceso mediante desreferenciación segura
if (opt_ptr) |p| {
std.debug.print("Valor a través del puntero: {}\n", .{p.*});
}
Explicación de la sintaxis:
*const i32indica un puntero a un entero que no puede modificarse.?*i32representa un puntero opcional que puede sernull.- El operador
.*desreferencia un puntero. - El patrón
if (opt_ptr) |p| { ... }es una forma segura de comprobar la presencia de un puntero.
Tipos Funcionales
En Zig, las funciones son primero clase de ciudadanos y su tipo se describe por los tipos de los parámetros y el tipo de retorno.
Definición de Funciones
fn add(a: i32, b: i32) i32 {
return a + b;
}
// Declaración de un puntero a función
var fn_ptr: fn (i32, i32) i32 = add;
// Llamada a través del puntero
const result = fn_ptr(3, 4);
std.debug.print("Resultado de add via ptr: {}\
