Zig vs Rust en 2026
(zackoverflow.dev)- Hace unos 3 años, cuando escribí directamente una VM de bytecode y un recolector de basura en Zig y
unsafe Rust, la ergonomía amigable para humanos de Zig llevaba ventaja, pero al entrar en la era de los agentes de programación, esa ventaja se volvió prácticamente irrelevante - La mejora de productividad para desarrolladores de 1.5x a 5x que ofrecen las funciones principales de Zig queda opacada por la productividad 100x que brindan los agentes de programación basados en Rust
- La interfaz de allocators de Zig, los enteros de ancho de bits arbitrario,
packed struct, comptime y otras funciones clave brillan sobre todo cuando una persona escribe el código directamente - El sistema de tipos de Rust es más eficaz para evitar en tiempo de compilación errores del agente mediante bounded polymorphism y la imposición de invariantes
- En una situación donde la cantidad de código generado por agentes aumentó 100 veces, la garantía de seguridad de memoria de Rust se vuelve una ventaja decisiva frente a Zig
Cambios clave
- Zig tenía una gran ventaja en la ergonomía del código
unsafe, pero a medida que disminuye la proporción de código escrito directamente por personas, también se reduce el valor práctico de esa ventaja - La mejora de productividad de 1.5x a 5x para desarrolladores humanos que aportan las funciones de Zig queda eclipsada por la mejora de 100x al usar agentes de programación en Rust
- Muchas de las funciones representativas de Zig mejoran la comodidad al escribir código manualmente, pero esa diferencia no importa tanto para los agentes de programación
- Hace unos 3 años, al escribir una VM de bytecode y un recolector de basura con Zig y
unsafe Rust, sentí que la experiencia de escribir códigounsafeera mejor del lado de Zig - Incluso en 2026, Zig sigue siendo un buen lenguaje, pero Rust pasó a ser un lenguaje más preferido y que además encaja mejor con los agentes de programación
La interfaz de allocator de Zig
- La interfaz de allocator de Zig permite aplicar fácilmente allocators especializados, como arena o stack fallback, para optimizar rutas de código específicas
- Al leer una línea de entrada del usuario, la longitud de la entrada es teóricamente ilimitada, así que se necesita un allocator de heap, pero en la práctica la mayoría de las entradas son búsquedas cortas o rutas y son mucho menores a 1 KB
std.heap.stackFallback(256, heap_allocator)coloca un buffer de tamaño fijo en el stack y solo pasa al heap si la entrada se desborda, lo que permite manejar el caso común sin allocation en heap- Antes, Rust no tenía una función equivalente a la interfaz
Allocatorde Zig, así que si necesitabas unVec<T>con un allocator personalizado, había que copiar la implementación de la biblioteca estándar y modificarla - El código fuente de las colecciones de Bumpalo tenía la forma de un fork de las colecciones estándar conectado a un bump allocator
- Durante un tiempo, Rust nightly tuvo el trait
Allocator, y ahora parece haber llegado a un nivel suficientemente bueno - El
Allocatorde Rust está basado en traits, así que usa despacho estático, a diferencia de los allocators de Zig, que están basados en vtable - Rust no tiene una convención generalizada en toda la comunidad de diseñar estructuras de datos en torno a parámetros de allocator como Zig, pero esa limitación importa menos ahora que la IA puede copiar y modificar código con facilidad
Enteros de ancho de bits arbitrario y packed struct
- Los enteros de ancho de bits arbitrario de Zig y
packed structfacilitan tareas como la optimización de caché de CPU con diseño orientado a datos, tagged pointers, NaN boxing y bitflags - Al usar una API de Obj-C con Metal mediante la Objective-C runtime C API,
idpuede ser un tagged pointer y no un puntero a objeto de heap alineado - Si se pasa un
NSNumbertagged a código que asume alineación, puede producirse UB, así que hay que verificar de forma barata si es “un puntero de heap o un inmediato tagged” - En un layout simplificado de tagged pointers de Objective-C, el bit inferior indica “no es un puntero de heap”, los siguientes 3 bits identifican el slot de clase y los 60 bits restantes forman el payload
- Zig puede expresar el layout de bits directamente como tipo usando
enum(u3)ypacked struct, comoclass: TaggedClass,payload: u60 - En Zig, con
@bitCastse puede pasar entre unu64crudo yObjcTaggedPointer, y enis_ns_numberse puede comprobaris_taggedy la clase.ns_number - El código equivalente en Rust usa constantes como
TAG_MASK,CLASS_MASK,CLASS_SHIFTyPAYLOAD_SHIFTdentro deObjcTaggedPointer(u64), hace operaciones OR al construir y aplica máscaras al acceder - En Rust, el slot de clase no es un tipo real sino una constante
u64, y escribirlo a mano es menos ergonómico que en Zig - En Rust, conviene más usar crates como bitfield o bitflags, pero ambos dependen de proc macros y no se sienten tan bien como
packed structde Zig - Con agentes de programación, el problema de que este tipo de código dé flojera escribirlo a mano se reduce bastante
El cambio en el valor de comptime
- comptime de Zig es su función más vistosa, y salvo algunos lenguajes crípticos con tipos dependientes, casi no hay lenguajes que ofrezcan una evaluación en tiempo de compilación tan buena como Zig
- En la práctica, dejé de extrañarlo tanto, y alrededor del 95% del uso era para crear estructuras de datos genéricas de tipos parametrizados
- Un patrón representativo es
fn ArrayList(comptime T: type) type, que recibe un tipo y devuelve un tipo de struct conitems: []T,capacity: usize,allocator: Allocator - El sistema de tipos de Rust reemplaza gran parte de los genéricos estilo comptime de Zig y además puede imponer más condiciones invariantes
- En el 5% restante de los casos, la falta de comptime sí resulta incómoda, y la única alternativa confiable es el codegen
- Si durante el desarrollo de un juego quieres hardcodear datos de geometría de hitbox generados por herramientas dentro de una estructura de datos, en Rust tienes que hacer que Claude escriba un script que genere un archivo Rust
- Aun así, la evaluación en tiempo de compilación no es algo que realmente se necesite con tanta frecuencia
Ventajas del sistema de tipos de Rust
- El sistema de tipos de Rust se considera un intercambio más valioso que
comptimede Zig, especialmente porque es fuerte en el área de traits/typeclasses para bounded polymorphism - Intentar implementar en Zig un nivel equivalente de bounded polymorphism resulta muy difícil
- El sistema de tipos de Rust puede imponer más condiciones invariantes (invariants), lo que ayuda a prevenir errores comunes de los agentes de programación
- En código de juegos, se usa el crate euclid para evitar un problema frecuente en programación gráfica: la confusión entre espacios de coordenadas
- Si creas tipos especializados para cada espacio de coordenadas, como
Point<Screen>oPoint<World>, mezclar por error coordenadas del mundo y de la pantalla queda bloqueado en compilación - Si
WorldPoint,WorldVectoryScreenPointson tipos separados, entonces se permite sumar point y vector del mismo espacio - Con
Translation2D::<f32, WorldSpace, ScreenSpace>se puede convertir explícitamente del espacio del mundo al espacio de pantalla - En cambio, código como
let bad: ScreenPoint = player;, que asigna directamente unWorldPointaScreenPoint, no está permitido
El efecto de lidiar menos con problemas de memoria
- Si los agentes de programación hacen posible escribir 100 veces más código, también aumenta 100 veces la cantidad de problemas de memoria que hay que revisar en código Zig
- Sin verificación formal, la superficie del espacio de búsqueda que hay que inspeccionar para encontrar bugs se vuelve mucho mayor
- En una situación como la actual, donde la cantidad de código generado creció, Rust resulta más atractivo
- El compromiso tradicional de Rust era que perjudicaba la productividad de los desarrolladores cuando todavía no estaban familiarizados con el borrow checker, pero con agentes de programación esa desventaja pierde mucha importancia
- Incluso usando
unsafeen Rust, puedes hacer que un agente de programación ejecute herramientas como miri para verificar si no ocurre UB ni se violan las reglas de aliasing de Rust
Conclusión
- Zig sigue siendo un lenguaje que se extraña y además es un buen lenguaje
- En la forma de trabajar de 2026, Rust es más preferido y además tiene mejor compatibilidad con los agentes de programación
1 comentarios
Comentarios en Lobste.rs
Un exlíder de mi equipo tenía la idea bastante firme de que el código copiado y pegado no siempre es algo malo
Por el principio DRY, intuitivamente sonaba incorrecto o al menos discutible, pero era una persona muy pragmática y aplicaba esta idea sobre todo a bases de código grandes de pruebas
Su lógica era que, en vez de forzar una interfaz compartida “inteligente”, una base de código simple pero más grande y con más duplicación podía ser más fácil de mantener
Últimamente, usando LLM, he vuelto a esa misma idea, y ahora la estoy aplicando incluso a partes más importantes del software
Generar código es rápido, y parece probable que los LLM también acierten más con una base de código simple pero muy duplicada
Si metes demasiada abstracción en los tests para reducir duplicación, se vuelven más difíciles de entender y además existe el riesgo de que queden sutilmente mal
Peor aún: si reutilizas la abstracción del código que estás probando, el test puede equivocarse de la misma forma que ese código
Además, a diferencia del código de aplicación, los tests prácticamente se “componen” gratis
Si no destrozaste seriamente el test harness, puedes agregar o borrar tests sin afectar a los demás, y como no hay fricción de integración, también hay una razón menos para evitar la duplicación
En pruebas he visto que esto se expresa como DAMP: “Descriptive and Meaningful Phrases”, un principio que enfatiza la legibilidad por encima de la singularidad
Este principio puede generar duplicación en forma de código parecido repetido, pero hace que los tests se vean más obviamente correctos
https://testing.googleblog.com/2019/12/…
En la comunidad de Go hay una idea parecida: “una pequeña copia es mejor que una pequeña dependencia” https://go-proverbs.github.io/
Le agradezco que comparta sus sesiones de live coding
Si recuerdo bien, al empezar algo a menudo buscaba el código más parecido a lo que quería hacer, lo copiaba completo y luego lo modificaba desde ahí
Yo pensaba: “¿no te vas a sentar un rato a pensar cuál es la abstracción compartida entre ambos?”, pero él simplemente seguía avanzando con copiar y pegar y era mucho más productivo que yo
Es interesante pensar en el momento del reescrito con IA de Zig → Rust de Bun https://xcancel.com/jarredsumner/status/2053063524826620129#m
Dice que 75 de ellos no habrían compilado en un lenguaje con destructores, semántica de movimiento y borrow checker
Básicamente, uno de cada tres PR desplegados era “se nos olvidó liberar algo en una ruta de error”
Unos 88 de los 108 estaban en Zig, y de los ~14 del lado de C++, la mayoría eran categorías residuales que seguirían existiendo en cualquier lenguaje, como ciclos de referencias y carreras de concurrencia con GC
Así que la diferencia Zig→Rust es real, y los bugs de Zig son exactamente del tipo que se corrige con destructores y ownership, mientras que el lado de C++ ya está cerca del piso
Sin garantías más fuertes en tiempo de compilación, esto seguirá siendo un juego del gato y el ratón
La propuesta es dejar de arreglar una por una la categoría de bugs más grande y eliminarla estructuralmente
– bun/docs/rust-rewrite-plan.md at claude/phase-a-port · oven-sh/bun · GitHub
No me queda claro qué quiere decir el autor con la parte de que “en el 5% restante de los casos, no tener comptime es doloroso, y la única forma de llegar reliably a un resultado equivalente es la generación de código”
Porque no dice nada sobre las macros procedurales
Es un poco engorroso construirlo bien, pero se pueden hacer muchas cosas
También creo que la generación de código a veces carga con mala fama injustificada
Con scripts
build.rshe resuelto con generación de código bastantes problemas fastidiosos, y ha funcionado bienClaro, quizá después me arrepienta
El argumento central del artículo se ve más o menos así
Rust sí es un buen lenguaje, pero aun así esto me parece exagerado
Parece publicidad de agentes de código
Esto viene de un artículo enlazado del mismo autor: un error muy común en programación gráfica es confundir los espacios de coordenadas, y el sistema de tipos es lo bastante potente como para expresar en tipos qué espacios de coordenadas y transformaciones son válidos
Lo mismo aplica a moneda, unidades SI frente a distancias y pesos del sistema imperial, cadenas validadas frente a cadenas provistas por el usuario y secretos, etc.
Además, si se gestiona bien, con tipos de estado se pueden impedir estados imposibles o no sound
Pero personalmente, lo que más quiero de Rust es la eliminación completa de las carreras de datos
Los lenguajes administrados también tienen carreras de datos
En “solo usa Go”, en Go todo es mutable por referencia entre hilos, además con gimnasia de slices incluida
Incluso JavaScript, que antes era un lenguaje totalmente de juguete y estaba entre los más puros y seguros, tiene en cada
awaituna carrera potencialY eso sin entrar en la maldad del patrón everything-is-an-EventEmitter
Así que sí. Si tan solo tuviera GC… 🤫
Siento que está ocultando un poco el punto clave
Los agentes de código también manejan Python y JavaScript muy bien
Si son mejores que Rust ya entra en terreno subjetivo, pero aun así no elegiría esos lenguajes para muchos trabajos
Me pregunto si el problema es que las funciones de Zig cambian con frecuencia, o si simplemente, por ser un lenguaje más nuevo, sus datos de entrenamiento de IA están más contaminados
Siento que Zig es más difícil de escribir que Rust, pero más fácil de leer
En la era de la IA, uno lee más código del que escribe, así que yo termino prefiriendo Zig
Decir que Rust es más atractivo con la cantidad de código que se está generando ahora es apenas el primer paso
Cuanto más código escriban las computadoras, más ventajosos serán los lenguajes más formales
Esto parece otra fase del debate sobre tipos dinámicos
Algo como: “los tipos dinámicos son más fáciles para los humanos, así que ¿por qué tendría que especificar lo mismo tres veces para las máquinas?”
Tipos, lifetimes… ¿qué otras cosas hacen que sea más fácil para las máquinas escribir y consumir código?
Me pregunto qué tan difícil se volverá para los humanos programar directamente en los lenguajes en los que las computadoras escribirán código