Zig no es otro lenguaje de sistemas que promete revolucionarlo todo. Es una herramienta concreta, con decisiones de diseño muy deliberadas, que resuelve problemas reales que C no resuelve bien y que Rust resuelve con bastante más fricción de la que a veces conviene. En 2025, con la versión 0.14.0 ya disponible, merece que le prestemos atención.
Qué es Zig y de dónde viene
Andrew Kelley empezó a diseñar Zig en 2015 y publicó las primeras versiones en 2016. La motivación era clara: quería un lenguaje de bajo nivel que fuera más honesto que C, sin las gotchas del preprocesador, sin comportamiento indefinido oculto y con un sistema de errores que obligara al programador a tomar decisiones explícitas.
No hay versión 1.0 todavía. Zig 0.13.0 salió en junio de 2024 y Zig 0.14.0 llegó en marzo de 2025. El proyecto es joven y la API estándar todavía cambia entre versiones, pero eso no impide usarlo para proyectos reales. TigerBeetle, la base de datos financiera de alta disponibilidad, está escrita íntegramente en Zig y lleva en producción desde hace años. Bun, el runtime de JavaScript más rápido del mercado, usa Zig como lenguaje principal.
Las decisiones de diseño que lo hacen diferente
Zig tiene cinco características que lo distinguen de cualquier otro lenguaje de sistemas:
- Sin GC. La gestión de memoria es manual y explícita. Los allocators se pasan como parámetro, lo que hace imposible que una biblioteca haga asignaciones de memoria a tus espaldas.
- Sin runtime oculto. El código Zig compila a código máquina directamente, sin ninguna capa intermedia que el programador no controle.
- Sin excepciones. Los errores son valores del tipo
error. Si una función puede fallar, lo dice en su firma. Si no manejas el error, el compilador te para. - Sin macros ni preprocesador. Lo que en C y C++ se hace con
#definey plantillas, en Zig se hace concomptime: ejecución real de código en tiempo de compilación. - Interop con C sin fricción. Puedes incluir cabeceras C directamente con
@cImporty usar cualquier biblioteca C como si fuera Zig nativo.
Instalación y primeros pasos
Descargar Zig es sencillo. La web oficial (ziglang.org/download) ofrece binarios precompilados para Linux, macOS y Windows. También hay una herramienta llamada zigup para gestionar versiones, muy útil si trabajas con proyectos que usan versiones distintas.
# Linux x86_64 - descargar e instalar Zig 0.14.0 wget https://ziglang.org/download/0.14.0/zig-linux-x86_64-0.14.0.tar.xz tar -xf zig-linux-x86_64-0.14.0.tar.xz sudo mv zig-linux-x86_64-0.14.0 /opt/zig export PATH=$PATH:/opt/zig # Verificar instalación zig version
El típico «hola mundo» en Zig:
const std = @import("std");
pub fn main() void {
std.debug.print("Hola, Zig!n", .{});
}
Para compilarlo:
zig run hola.zig
La gestión de memoria desde el primer día
Una de las primeras cosas que llaman la atención cuando se aprende Zig es que la biblioteca estándar no usa memoria dinámica por su cuenta. Cada función que necesita memoria pide un allocator como parámetro. Esto puede parecer verboso al principio, pero tiene una ventaja enorme: sabes exactamente qué código asigna memoria y qué código no.
const std = @import("std");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const lista = try std.ArrayList(u32).init(allocator);
defer lista.deinit();
// Aquí trabajamos con la lista
try lista.append(42);
std.debug.print("Primer elemento: {d}n", .{lista.items[0]});
}
El GeneralPurposeAllocator detecta fugas de memoria en modo debug. En producción puedes cambiarlo por std.heap.page_allocator o un ArenaAllocator según las necesidades. Tienes más detalles sobre este sistema en el artículo sobre allocators y arenas de esta misma serie.
El sistema de errores
Las funciones que pueden fallar devuelven un error union: el tipo !T, donde ! indica que puede ser un error o un valor de tipo T. Para manejar el error puedes usar try, que propaga el error al llamador, o catch, que te permite manejarlo en el sitio.
const std = @import("std");
fn leerEntero(texto: []const u8) !i32 {
return std.fmt.parseInt(i32, texto, 10);
}
pub fn main() !void {
// try propaga el error si falla
const n = try leerEntero("42");
std.debug.print("Número: {d}n", .{n});
// catch permite manejar el error localmente
const m = leerEntero("no_es_un_numero") catch |err| {
std.debug.print("Error: {}n", .{err});
return;
};
std.debug.print("Número: {d}n", .{m});
}
Esta aproximación a los errores es similar a la de Rust con Result<T, E>, pero más ligera. No hace falta definir un tipo de error explícito para cada función; el compilador infiere el conjunto de errores posibles. Puedes leer más sobre cómo se compara con Rust en el artículo sobre Rust 2024 Edition.
El sistema de build integrado
Zig no necesita CMake, Make ni Ninja. El sistema de build está escrito en Zig puro y se define en un fichero build.zig. Un proyecto básico tiene esta pinta:
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const exe = b.addExecutable(.{
.name = "mi-programa",
.root_source_file = .{ .path = "src/main.zig" },
.target = target,
.optimize = optimize,
});
b.installArtifact(exe);
}
Con zig build compilas el proyecto. Con zig build -Dtarget=aarch64-linux-musl compilas para ARM64 Linux desde tu máquina x86_64. La cross-compilation es nativa, no requiere toolchains externos.
Por qué Zig en 2026
El argumento a favor de Zig no es que sea el mejor lenguaje abstractamente. Es que, para ciertos casos de uso, encaja mejor que las alternativas. Si necesitas código de bajo nivel con interop directo con C, Zig es considerablemente más cómodo que Rust. Si necesitas cross-compilation sin dolor de cabeza con toolchains, Zig gana a casi todo. Si quieres metaprogramación sin macros ni templates crípticos, comptime es una solución elegante.
La comunidad sigue siendo pequeña comparada con Go o Rust, pero está creciendo. El gestor de paquetes oficial (zig fetch) llegó en versiones recientes y el ecosistema de bibliotecas va madurando. No es todavía el lenguaje para proyectos donde la estabilidad de la API sea crítica, pero para proyectos nuevos o para embeberlo como compilador C alternativo, ya tiene sentido apostar por él.
Imagen: Pexels / Tima Miroshnichenko
