- A partir de las diferencias de filosofía y valores entre los tres lenguajes, se compara qué problemas intenta resolver cada uno
- Go se describe como un lenguaje que prioriza la simplicidad y la estabilidad, minimizando funciones para facilitar la colaboración y el mantenimiento
- Rust busca seguridad y rendimiento al mismo tiempo, y garantiza la seguridad de memoria mediante un sistema de tipos complejo y una estructura de traits
- Zig se presenta como un lenguaje experimental que otorga control total al desarrollador mediante gestión manual de memoria y diseño orientado a datos
- Los enfoques contrastantes de los tres lenguajes revelan el sistema de valores que implementa un lenguaje de programación, y el criterio de elección pasa por con qué filosofía se identifica cada desarrollador
Perspectiva para comparar lenguajes
- El autor intenta entender el sistema de valores de cada lenguaje a través de experimentar con lenguajes nuevos, no con el que usa en el trabajo
- Se enfatiza que, más que comparar listas de funciones, lo importante es qué trade-offs eligió cada lenguaje
- Go, Rust y Zig comparten muchas capacidades a nivel funcional, pero los valores priorizados por sus diseñadores son distintos
- Al comprender la filosofía de cada lenguaje, es posible juzgar para qué entornos y objetivos resulta adecuado
Go — un lenguaje centrado en la simplicidad y la colaboración
- Go se distingue por su minimalismo, con la característica de que “puedes llevar todo el lenguaje en la cabeza”
- Los genéricos se añadieron después de 12 años, y todavía no existen funciones como tagged unions o syntactic sugar para manejo de errores
- Es muy cauteloso al agregar funciones, por lo que hay bastante código repetitivo, pero a cambio ofrece estabilidad y legibilidad en el lenguaje
- Los slices de Go abarcan funciones equivalentes a
Vec<T> en Rust o ArrayList en Zig, y el runtime administra automáticamente la ubicación en memoria
- Fue diseñado con el objetivo de ser simple y de compilación rápida, como respuesta a la complejidad de C++ y a la lentitud al compilar
- Da prioridad a la eficiencia de colaboración en entornos corporativos, favoreciendo código claro y consistencia por encima de funciones complejas
Rust — complejo, pero con seguridad y rendimiento poderosos
- Rust se presenta con la idea de “abstracciones de costo cero”, y es un lenguaje maximalista donde se combinan muchos conceptos
- La razón por la que es difícil de aprender es su alta densidad conceptual, con un sistema de tipos complejo y una estructura de traits
- El objetivo central de Rust es compatibilizar rendimiento y seguridad de memoria
- Para evitar UB (Undefined Behavior), realiza verificaciones en tiempo de compilación
- Bloquea comportamientos impredecibles causados por referencias inválidas de punteros o doble liberación, entre otros casos
- Para que el compilador pueda entender el comportamiento del código en runtime, el desarrollador debe definir explícitamente tipos y traits
- Gracias a esta estructura, hay mayor confianza en el código de otras personas, y el ecosistema de librerías se mantiene muy activo
Zig — control total y diseño orientado a datos
- Zig es el lenguaje más reciente de los tres; está en la versión 0.14 y casi no tiene documentación de la biblioteca estándar
- Adopta gestión manual de memoria, por lo que el desarrollador debe llamar directamente a
alloc() y elegir un allocator
- A diferencia de Rust o Go, permite crear variables globales con facilidad, y detecta “illegal behavior” en runtime para detener el programa
- Con 4 modos de release seleccionables al compilar, es posible ajustar el equilibrio entre rendimiento y estabilidad
- Excluye deliberadamente las funciones de programación orientada a objetos (OOP)
- No hay campos private ni dynamic dispatch, y ni siquiera
std.mem.Allocator está implementado como interfaz
- En su lugar, apunta al diseño orientado a datos (data-oriented design)
- También en la gestión de memoria, en vez del control detallado por objeto al estilo RAII, recomienda una estructura que asigna y libera periódicamente bloques grandes de memoria
- Zig se describe como un lenguaje de carácter libre y antisistema, que elimina la forma de pensar de la OOP y maximiza el control en manos del desarrollador
- Actualmente el equipo está concentrado en reescribir todas las dependencias, y la versión estable (1.0) sigue sin fecha definida
Conclusión — las diferencias de valores que revelan los lenguajes
- Go toma como valores centrales la colaboración y la simplicidad, Rust la seguridad y el rendimiento, y Zig la libertad y el control
- Las diferencias entre los tres no son una simple comparación de funciones, sino el reflejo de una elección filosófica sobre el desarrollo de software
- Los desarrolladores terminan eligiendo un lenguaje según con qué valores se identifican
1 comentarios
Opinión de Hacker News
En Rust no es difícil crear una variable global mutable
solo hay que usar
unsafeo un smart pointer que provea sincronización.Rust es re-entrant por defecto y garantiza la seguridad entre hilos en tiempo de compilación.
Si no te importa la seguridad estática entre hilos, se puede hacer tan fácilmente como en Zig o C.
La diferencia es que Rust ofrece más herramientas de garantía sobre el comportamiento del código en tiempo de ejecución.
Cuando vuelvo a otros lenguajes y veo que esto se usa con total naturalidad, me parece una locura desde el punto de vista de la seguridad.
Pero cuando se acumulan estas “cosas simples”, dejan de ser simples.
Rust ya cruzó esa línea y ahora ya no es trivial en absoluto.
Si es así, me parecería más atractivo que C.
También quisiera saber cómo se maneja cuando dos variables siempre tienen que bloquearse juntas.
Al depurar, casi siempre terminaban siendo la raíz del problema.
Sobre el comentario de que Rust tiene una alta densidad conceptual, en la práctica creo que con solo conocer el 5% ya se puede usar de forma productiva.
Llevo más de 12 años usando Rust y nunca he tenido que usar algo como
#[fundamental].En Rust también se puede hacer arena allocation, y existe el concepto de allocator.
Simplemente hay un allocator por defecto, y normalmente se usan asignaciones explícitas en heap como
Box::new.Una global mutable se puede crear como
static FOO: Mutex<T> = Mutex::new(...), y el mutex es necesario por seguridad de memoria.El sistema de tipos de Rust está diseñado para garantizar no solo la seguridad de memoria, sino también la seguridad semántica del código.
En C hay menos de esta complejidad.
La complejidad al final sí importa.
No se trata solo de si se puede o no, sino de una diferencia en el estilo básico de programación.
También hubo un caso en que la Zig Software Foundation citó incorrectamente comentarios de Asahi Lina sobre Rust.
No me gusta mucho la actitud de marketing de Zig de rebajar a otros lenguajes.
Lo que me gusta de Zig es que es un lenguaje que permite manejar el agotamiento de memoria de forma elegante.
Asume que toda asignación puede fallar y exige manejarlo explícitamente.
Tampoco trata el espacio de stack como si fuera magia: el compilador analiza el grafo de llamadas e infiere el tamaño máximo.
En entornos embebidos, este diseño centrado en recursos es esencial.
No es algo que se resuelva con manejo a nivel de lenguaje.
Al final ambos comparten el mismo problema de la gestión manual de memoria.
Entonces, creo que es mejor usar un lenguaje con GC.
Solo que la biblioteca estándar de Rust usa panic ante OOM, así que existe un ecosistema aparte para desarrollo embebido en entornos no-std.
El slice de Go es distinto de
Vec<T>en Rust.append()devuelve un nuevo slice, que puede compartir la memoria existente o no.No hay forma de reducir memoria, y si solo escribes
append(s, ...)terminas ignorando el nuevo slice.Go tiene una actitud de “haz lo que te digo”, mientras que Rust tiene una de “verifica si hiciste lo que te dije”.
Es decir, Go permite errores a cambio de simplicidad, y Rust elige reducir errores aunque eso implique más complejidad.
Además, si solo escribes
append(s, ...)obtienes un error de compilación, así que el texto original hace una afirmación algo inexacta.Go es un lenguaje que evalúa con cuidado el aumento de complejidad al agregar funciones.
append(s, …)ni siquiera compila, no creo que un principiante pueda cometer ese error.Probablemente porque no es tan común pasar directamente listas expandibles.
Muchas veces la sorpresa viene simplemente de no haber leído la documentación.
Creo que en la práctica es difícil detectar la UB (Undefined Behavior) de C/C++ con verificaciones en tiempo de ejecución.
Android también aplicó sanitizers a todos los commits, pero solo después de migrar a Rust comenzaron a disminuir los exploits.
Me gustó que el artículo comparativo tratara con honestidad las fortalezas y debilidades de cada lenguaje.
Aunque fue una lástima que no mencionara a Raku.
En mi opinión, si C–Zig–C++–Rust–Go forman un continuo de lenguajes de bajo nivel, del lado de alto nivel sería Julia–R–Python–Lua–JS–PHP–Raku–WL.
Soporta definición de gramáticas a nivel de lenguaje, lo que facilita crear DSL o hacer parsing de logs.
Al estar basado en una VM, su rendimiento es más bajo, pero es adecuado para expresar directamente la estructura del problema.
Como sucesor de Perl, busca ser un lenguaje flexible y coherente.
Pensar que en Rust una función que devuelve un puntero provoca automáticamente una asignación en heap es un malentendido.
Las variables locales están en el stack y desaparecen al retornar, así que el puntero queda invalidado.
En modo seguro, Rust no permite desreferenciar punteros, y en modo unsafe el desarrollador asume la responsabilidad de garantizar su validez.
Probablemente se confundió
Box::newcon una “asignación implícita”.Parece o una mala comprensión del concepto o un intento deliberado de desinformar.
La mayor ventaja de Go es su modelo de concurrencia simple.
Gracias a las goroutines, es fácil escribir código paralelo.
Encontrar implementaciones de interfaces puede ser difícil, pero la legibilidad es alta y eso favorece el trabajo en equipo.
No hay colored function, y la comunicación basada en canales es simple, así que se puede escribir código concurrente correcto con rapidez.
Artículo relacionado: Structured Concurrency or Go Statement Considered Harmful
std.Iode Zig es parecida al modelo de concurrencia de Go.La palabra clave
gocorresponde astd.Io.async, los canales astd.Io.Queueyselectastd.Io.select.Lo que yo quiero es un lenguaje que combine la simplicidad de Go con el manejo de resultados/errores/enums de Rust y mejores genéricos.
He visto OCaml, D, Swift, Nim y Crystal, pero todavía no hay ninguno que domine el mercado.
En su lugar, valdría la pena mirar Gleam.
Ojalá surja alguna mejora que resuelva este problema recurrente.
Los genéricos probablemente seguirán siendo un desafío difícil.
Me gustó el tono general del artículo porque transmitía la pasión y curiosidad de un desarrollador principiante.
Creo que la ausencia de genéricos en Go no fue un simple minimalismo, sino el resultado de pensar en los trade-offs.
Los lifetime de Rust fueron el mayor obstáculo para mucha gente, y la innovación del lenguaje está en la combinación de conceptos existentes.
La gestión manual de memoria en Zig se basa más en la filosofía de Data-Oriented Design (DOD) que en excluir OOP.
Charla relacionada: presentación de Andrew sobre DOD
la cuestión central era “qué prefieres: programadores lentos, compiladores lentos o ejecución lenta”.
Parece que el equipo de Go finalmente encontró un punto medio satisfactorio.