← Ir a Aprender

¿Por que Zig cuando ya existen C++, D y Rust?

Sin control de flujo oculto

Si un fragmento de código Zig no parece estar saltando a hacer una llamada a una función, es por que no lo está haciendo. Esto significa que podemos estar seguros de que el siguiente código llama solo a foo() y después a bar() y esto está garantizado sin tener que conocer tipo de dato alguno:

var a = b + c.d;
foo();
bar();

Ejemplos de control de flujo oculto:

El propósito de esta decisión de diseño es mejorar la legibilidad del código.

Sin asignaciones de memoria ocultas

Zig tiene un enfoque de no intervención cuando se trata de asignaciones de memoria. No existe palabra reservada new u otra característica que haga uso de asignaciones de memoria (como operadores de concatenación de cadenas[1]). Todo el concepto de 'heap' está manejado por bibliotecas o el código de cada aplicación y no por el lenguaje.

Ejemplos de asignaciones ocultas:

Casi todos los lenguajes con 'garbage collection' incurren en asignaciones de memoria ocultas, dado que el recolector de basura esconde la evidencia al momento de limpiar.

El problema principal con con las asignaciones de memoria ocultas es que previene la reusabilidad de porciones de código, limitando el número de ambientes en los que dicho código podría funcionar. Dicho de una forma simple, existen casos de uso en los cuales uno debería poder confiar en el control de flujo y los llamados de función no deberían tener efectos colaterales de asignación de memoria, por lo tanto un lenguaje de programación solamente puede servir a estos casos de uso si realmente aporta tal garantía.

En Zig, existen características de la biblioteca estándar que proveen y admiten 'heap allocators' (asignadores de memoria en el heap), pero estas son características opcionales de la biblioteca estándar y no construcciones internas del lenguaje mismo. Si nunca inicializas un heap allocator, puedes tener la certeza de que tu programa no hará asignaciones de memoria en el heap.

Cada funcionalidad de la biblioteca estándar que requiere asignación de memoria en heap acepta un parámetro Allocator para poder operar. Esto quiere decir que la biblioteca estándar de Zig soporta arquitecturas no especificas. Por ejemplo, std.ArrayList y std.AutoHashMap puedes usarlas para programación de bajo nivel en procesadores/microcontroladores(bare metal programming).

Los allocators (asignadores de memoria) personalizados hacen que el manejo manual de memoria sea muy sencillo. Zig ofrece un allocator que mantiene seguridad de la memoria a primera vista frente a bugs de tipo doble liberación de objetos(double-free) y uso después de liberación de objetos(use-after-free). Detecta automáticamente fugas de memoria e imprime un stack trace (traza de pila). Otro allocator disponible es un "arena allocator" que permite asignar cualquier cantidad de espacios de memoria en una sola y da la facilidad de liberarlos todos al mismo tiempo en lugar de tener que manejar las liberaciones de cada asignación individualmente. Los allocators de propósito especial pueden usarse para mejorar el desempeño de manejo de memoria en casos particulares.

[1]: De hecho existe un operador de concatenación de strings (en general es un operador de concatenación de arreglos-arrays-), pero solo funciona en tiempo de compilación y por ello, no incurre en asignaciones de memoria en heap en tiempo de ejecución.

Soporte de primera clase para biblioteca no estándar

Como se sugiere arriba, Zig incluye una gran biblioteca estándar opcional. Cada API de la librería librería se compila solamente si la usas en tu programa. Zig soporta además efectuar linking opcional hacia libc. Zig es amigable para desarrollo de proyectos de muy bajo nivel(bare-metal) y proyectos orientados a alto rendimiento.

Es lo mejor de ambos mundos; por ejemplo, en Zig, los programas de WebAssembly pueden usar las características de la biblioteca estándar y aún así generar binarios muy pequeños en comparación con los generados por otros lenguajes que soportan compilar a WebAssembly.

Un lenguaje portable para bibliotecas

Uno de los santos griales de la programación es la reusabilidad del código. Desafortunadamente, en la práctica, nos encontramos con frecuencia reinventando la rueda. Algunas veces es justificable.

Un manejador de paquetes y sistema de compilación para proyectos existentes

Zig es una cadena de herramientas(toolchain) además de ser un lenguaje de programación que incluye un sistema de compilación y un manejador de paquetes diseñados para ser útiles incluso en el contexto de proyectos C/C++ tradicionales.

No solo puedes escribir código Zig en lugar de código C o C++, sino también usar Zig como un reemplazo para autotools, cmake, make, scons, ninja, etc. Además de esto, provee un manejador de paquetes para dependencias nativas. Este sistema de compilación está diseñado para ser útil aún cuando la totalidad del código de un proyecto está escrito en C o C++. Por ejemplo, portar ffmpeg al sistema de compilación de Zig, se vuelve posible compilar ffmpeg en cualquier sistema soportado hacia cualquier sistema soportado usando tan solo una descarga de 50 MiB. Para proyectos de fuente abierta, esta habilidad ágil para armar desde las fuentes - e incluso con compilación cruzada(cross-compile) - puede ser la diferencia entre ganar o perder contribuciones valiosas.

Los manejadores de paquetes como apt, pacman, homebrew y otros, son fundamentales en la experiencia de usuario final, pero pueden ser insuficientes para las necesidades de los desarrolladores de software. Un manejador de paquetes de lenguajes específico puede ser la diferencia entre tener muchos o ningún contribuyente. Para proyectos de fuente abierta, la dificultad de hacer que un proyecto compile es una gran carga para potenciales contribuyentes. Para proyectos de C/C++ tener dependencias puede ser fatal, especialmente en Windows, donde no hay manejadores de paquetes. Incluso para compilar el lenguaje y herramientas Zig, la mayoría de contribuyentes potenciales encuentran difícil la dependencia con LLVM. Zig ofrece un mecanismo con el cual los proyectos dependen de bibliotecas nativas directamente - sin importar si el usuario tiene o no una versión correcta de su manejador de paquetes del sistema y de forma tal que prácticamente se garantiza que el proyecto compilará en el primer intento sin importar que plataforma se esté utilizando o para qué plataforma se esté armando.

Otros lenguajes tienen manejadores de paquetes pero no eliminan sistemas de dependencias problemáticos como lo hace Zig.

Zig puede reemplazar un sistema de compilación de un proyecto con un lenguaje razonable que aporta una API declarativa para compilar proyectos y que al mismo tiempo aporta un manejador de paquetes y, por ende, la habilidad de depender de otras bibliotecas de C. La habilidad de tener dependencias permite llegar a niveles de abstracción más altos y así la proliferación de código reusable de alto nivel.

Simplicidad

C++, Rust y D tienen una cantidad tan grande de características que pueden ser un distractor del verdadero significado de la aplicación en la que estás desarrollando. Es fácil verse depurando el propio conocimiento del lenguaje en lugar de estar depurando la aplicación en si.

Zig carece de macros pero es lo suficientemente poderoso para expresar programas complejos en una forma clara y no repetitiva. Incluso Rust, que cuenta con casos especiales de macros como format!, implementado dentro del mismo compilador. Mientras tanto en Zig, la función equivalente está implementada en la biblioteca estándar sin casos especiales de código en el compilador.

Herramientas

Puedes descargar Zig desde la sección de descargas. Zig provee binarios para Linux, Windows, y MacOs. En seguida se describe qué obtienes con cada uno de estos archivos: