36 puntos por GN⁺ 2025-08-23 | 1 comentarios | Compartir por WhatsApp
  • Rust es un lenguaje donde varios conceptos están estrechamente entrelazados entre sí, por lo que incluso para entender un programa básico hay que aprender muchos elementos al mismo tiempo
  • Funciones, genéricos, enums, pattern matching, traits, referencias, ownership, Send/Sync, Iterator, etc., son todos elementos centrales diseñados para interactuar entre sí
  • En comparación con JavaScript, en JS se puede escribir código con solo conocer algunos conceptos, pero en Rust solo es posible escribir código realmente significativo cuando se entiende el contexto completo del lenguaje
  • Esta complejidad de Rust eleva la barrera de aprendizaje, pero al mismo tiempo ofrece seguridad y consistencia, y tiene un gran impacto en la forma de diseñar el código
  • Esta estructura del lenguaje es lo que hace especial a Rust, y la visión de un “Rust más pequeño” nos hace volver a pensar en una filosofía de lenguaje cuidadosamente ensamblada

La dificultad de aprender Rust

  • Aunque Rust tiene una barrera de entrada alta, muchas personas han contribuido a mejorar la documentación, las APIs y los diagnósticos
  • Entre sus conceptos básicos están funciones como objetos de primera clase, enums, pattern matching, genéricos, traits, referencias, borrow checker, seguridad en concurrencia e iteradores
  • Estos conceptos dependen unos de otros y están entrelazados, por lo que es difícil aprenderlos por separado, y la biblioteca estándar también aprovecha la mayoría de estas funciones
  • Incluso para entender unas 20 líneas de código Rust, hay que captar al mismo tiempo varios elementos como el paradigma funcional, Result y el manejo de errores, tipos genéricos, enums e iteradores

Comparación entre Rust y JavaScript

  • Al escribir el mismo programa de detección de cambios en archivos en Rust y en JS, en Rust se entrelazan numerosos conceptos del lenguaje
  • En JS, básicamente basta con entender las funciones y el manejo de null para poder escribir código funcional
  • Esto no significa simplemente que Rust sea más difícil, sino que muestra que Rust es un diseño que exige una comprensión estructural del lenguaje en su conjunto

El diseño estrechamente acoplado de Rust

  • El núcleo de Rust es la combinación de funciones diseñadas de forma orgánica
    • Los enums resultan incómodos sin pattern matching, y el pattern matching también es limitado sin enums
    • Result e Iterator no pueden implementarse sin genéricos
    • Los conceptos Send/Sync y las restricciones de println solo pueden expresarse de forma segura con traits
    • El borrow checker garantiza la seguridad de Send/Sync mediante el análisis de captura de closures
  • Este acoplamiento convierte a Rust no simplemente en un conjunto de funciones, sino en un sistema de lenguaje integrado

La visión de un Rust más pequeño

  • En 2019, without.boats mencionó “Smaller Rust” y discutió la posibilidad de un Rust pequeño y refinado
  • Hoy Rust es mucho más grande, pero la idea de un Rust pequeño nos recuerda la esencia de un diseño de lenguaje cuidadosamente engranado
  • El atractivo de Rust está en que sus elementos del lenguaje son independientes entre sí y, al combinarse, ofrecen una poderosa expresividad y seguridad

Conclusión

  • Rust es difícil de aprender, pero la consistencia e integración de sus conceptos entrelazados funcionan como una gran fortaleza
  • Gracias a esta estructura, Rust hace que los desarrolladores no solo escriban código, sino que también adopten una forma de pensar que considera al mismo tiempo la seguridad y el rendimiento
  • La esencia de Rust está en un “lenguaje central pequeño y sofisticado”, y esa sigue siendo una filosofía importante incluso en el Rust ampliado de hoy

1 comentarios

 
GN⁺ 2025-08-23
Opiniones de Hacker News
  • Me parece irónico que incluso un programa JS "simple" tenga bugs. En la documentación de fs.watch se indica explícitamente que en el callback hay que verificar obligatoriamente si filename puede ser null. En Rust, este hecho quedaría reflejado en el sistema de tipos y te obligaría a manejarlo, pero en JS es muy fácil escribir código al tanteo. Documentación relacionada
    • Si usas Typescript, la verificación de null se vuelve obligatoria. Por eso me parece un buen ejemplo de cómo TS es un paso relativamente liviano que acerca a JS un poco más a la corrección del lado de Rust
    • También hay otro bug: for path in paths debería ser for (const path of paths). En JS, sin paréntesis da error de inmediato, pero la diferencia entre in y of es que recorre índices del iterable, no valores, así que en la práctica terminaría pasando el índice convertido a string como primer argumento de fs.watch. Incluso TypeScript podría no detectar este error
    • Como ya señalaron, la sintaxis del bucle en sí es incorrecta, y bastaría con ejecutarlo para darse cuenta rápido. O sea, probablemente convenga asumir que el autor no escribió ese código JS con cuidado y que no era algo importante para el punto central
    • Puede que se me haya pasado, pero me pregunto de dónde salió kind. En console.log("${kind} ${filename}") debería ser eventType (una cadena), no kind
  • Quiero hacer una observación menor. println de Rust solo puede imprimir tipos que implementan los traits Display o Debug. Por eso Path no se puede imprimir directamente. No todos los sistemas operativos guardan rutas compatibles con UTF-8, mientras que todos los tipos string de Rust son UTF-8. O sea, imprimir un Path puede implicar pérdida de información. Path devuelve mediante el método display un tipo que implementa Display. Rust integró esto en el sistema de tipos, pero en JS/TS es difícil expresar que internamente las cadenas son UTF-16, y para manejar correctamente rutas no Unicode hay que usar directamente TextEncoder/TextDecoder. Por experiencia pasada, si un servidor enviaba texto en Shift_JIS y lo leías con response.text(), en runtime solo salía una cadena vacía. Si no estás acostumbrado a los problemas de codificación, puedes pasarte días depurando algo así. Y el ejemplo en JS tiene bugs y errores de sintaxis que no están en el código Rust (en el bucle hace falta for-of en vez de for-in). Tampoco diría que este ejemplo use solo "funciones de primera clase"; igual que en Rust, requiere entender iteradores, y además usa CommonJS. También hay que aprender async/await, Promises y top-level await, y este último recién fue soportado hace poco en algunos runtimes, incluido node. Aún hay motores JS que no lo soportan (por ejemplo, Hermes de React Native)
    • Ese tipo de cosas es la razón por la que sigo usando Rust. Es solo un ejemplo, pero estos problemitas y trampas están por todos lados en otros lenguajes. Puede que cada uno por separado no ocurra, pero si se acumulan a lo largo de la vida del programa, los bugs raros empiezan a aparecer de la nada y uno tiene que seguir cazándolos sin parar. En Rust eso no pasa. El sistema de tipos bloquea de antemano una cantidad absurda de casos. De hecho, cuando sacas software hecho en Rust con la funcionalidad ya completa, luego solo agregas features de vez en cuando y casi desaparece el trabajo típico de corregir bugs. Claro, siempre puede haber errores lógicos en cualquier parte, pero te corta de raíz los problemas causados por desajustes tontos de tipos o estructura que en otros lenguajes son comunes, así que la productividad y el mantenimiento se sienten completamente distintos

    • Personalmente siento que no hay tantos desarrolladores JS/TS que entiendan de verdad thenable/Promise y async-await. Incluso he visto cosas como esto:

      var fn = async (param) => new Promise((res, rej) => {
        fooLibraryCall(param).then(res).catch(rej);
      });
      

      Envueltan tal cual un wrapper con callbacks dentro de una Promise y luego lo vuelven a usar dentro de una función async. Cada vez que veo esto me duele el alma. En serio he visto código así por todos lados. Y si además sumas imports de módulos, import() asíncrono, transpilación, code splitting, etc., la cosa se vuelve realmente compleja

  • Creo que la cita de Bjarne en realidad es un argumento de venta para justificar repetidamente cómo C++ se vuelve cada vez peor. Puede que al principio fuera sincero, pero ahora el patrón se repite. Lo veo así:
    1. "Dentro de C++ hay un lenguaje más pequeño y más limpio"
    2. Pero como no se puede extraer un subconjunto del lenguaje, propone primero hacer un superconjunto (más funcionalidades) y después hacer el subconjunto
    3. El superconjunto entra en el nuevo C++N+1. Luego dicen que la discusión del subconjunto real va después, y así lo siguen postergando
    4. C++N+1 se vuelve más complejo, y esto se repite para siempre A quienes han visto esto repetirse les cuesta entender por qué la gente sigue ahí. Al final, ese "lenguaje más pequeño y más limpio" nunca llega. Siempre se repite el paso uno
    • Me recuerda a xkcd 927 xkcd 927. Cada versión del estándar de C++ se vuelve más compleja; hay cambios buenos, sí, pero también encajan mal con versiones anteriores y el código fuente se vuelve cada vez más caótico. Mantengo dos librerías OSS, pero ya casi no lo uso. Últimamente pienso mucho cuánto más vale la pena seguir aguantando. Rust se siente realmente refrescante después de venir de c++11/14/17/20. Eso sí, Rust también es bastante enorme si no conoces todo el panorama. Lo que se señala en este artículo me parece muy acertado
  • ¿A nadie más le distrajo de inmediato ver el shebang (script Rust autoejecutable)? Me sorprendió igual que cuando vi lo mismo antes en Go. Se ve bastante útil y parece que podría servir perfectamente para usos básicos. También vi algo parecido en un proyecto que gestionaba el pipeline de build/test con rust. Para ese tipo de uso puede ser una alternativa bastante buena. Eso sí, yo normalmente, cuando necesito un script que va un poco más allá de bash, uso Deno+TS. Es el lenguaje con el que más tiempo he trabajado (28 años), y luego C# por 24 años. Uso Node desde sus inicios. Deno es más fácil de administrar que Node o Python en cuanto a compartir y centralizar paquetes. El frontmatter de cargo funciona de forma parecida
    • Yo fui quien diseñó/implementó directamente la integración de script en cargo (aunque ya había muchas implementaciones de terceros). Me alegra muchísimo ver casos de uso reales y confirmar que se esté mencionando. También revisen la documentación. Hubo discusiones larguísimas sobre qué forma era la adecuada, cómo debía integrarse con el lenguaje y qué alcance debía tener la primera versión. Ahora estamos cerrando detalles como la guía de estilo y actualizaciones a la referencia de Rust, y lo grande que falta es pulir detalles de rustfmt, rust-analyzer, corregir bugs en rustc y mejorar los reportes de error en Cargo. Yo mismo escribo todos los días scripts de reproducción de issues con cargo script
    • De hecho, empecé a investigar buscando la keyword de la feature -Zscript y me distraje con eso. Lleva en marcha desde 2023 y hay issues abiertos que ya parecen bastante cerca de completarse. También vi en el repositorio de ZomboDB que manejan el pipeline de build en rust, aunque no entendí por completo todo el contexto. Quiero mencionar que el frontmatter de cargo es increíblemente útil para la portabilidad de scripts. Solo hace falta compartir un archivo, y a diferencia de Python o Node.js, puedes traer dependencias y usarlas de inmediato sin instalación ni inicialización adicional
    • Dijeron que en Go también se puede hacer lo mismo, pero me gustaría saber si alguien puede explicarlo con más detalle. Si es este enlace, a mí también me interesa
    • Aunque he usado JS y C# durante mucho tiempo, en 2025 no elegiría un sistema por ese tipo de razones. En los últimos 20 años muchísimas cosas han mejorado muchísimo
    • Eso es simplemente una funcionalidad básica de Unix. Un archivo que empieza con #!/some/path hace que el shell simplemente le pase el archivo completo por stdin al comando indicado y lo ejecute
  • Cuando dicen que "dentro de Rust está saliendo un lenguaje más pequeño y más limpio", me pregunto cuál es exactamente ese lenguaje. Por lo que se ve en el texto, todavía harían falta referencias, lifetimes, traits, enums, etc. para que funcione, y entonces no sería muy distinto de Rust. En la última parte aparecen dos pistas, "el Rust que quiero usar" y "el Rust del pasado", pero no me terminan de convencer. También leí "Notes on a smaller Rust" de withoutboats, pero como objetivo de diseño es distinto de Rust; más que intentar ser Rust, muestra lecciones que pueden sacarse de Rust al pensar en diseñar un lenguaje nuevo. No es un lenguaje que busque ser Rust, sino un ejemplo de lenguaje ajustado a exigencias "mainstream" (por ejemplo, GC, simplificación de compilación/sintaxis, etc.). Segundo, también aparece la idea de que "el lenguaje del que me enamoré cuando lo aprendí en 2018 era ese 'Rust más pequeño'", pero en la práctica Rust no ha cambiado tanto en lo esencial desde 2018. La mayoría de los cambios, como las editions, han sido mejoras de flexibilidad sintáctica, y las únicas excepciones realmente grandes son async y const. Entonces, me habría gustado que dijera más directamente: "Rust antes de async y const era más pequeño y más limpio", porque el texto no lo explica así de frente
    • Si se habla de un 'Rust más pequeño y más limpio', se me viene a la mente Austral como ejemplo
    • También existe el argumento de que es posible un lenguaje más simple (más pequeño) conservando los conceptos centrales de Rust. Por ejemplo, si quitas el trait Copy, el reborrowing, la coerción por deref, el into_iter automático en bucles, la llamada automática a drop al terminar el scope (podrías llamarlo tú mismo o hacer que el compilador dé error), el :Sized implícito por defecto en trait bounds, la omisión de lifetimes (lifetime elision), la ergonomía de match y otras automatizaciones/conveniencias, podrías tener un Rust realmente más simple en términos mecánicos. Pero un lenguaje así sería muy incómodo para usarlo en el día a día. Irónicamente, muchos de esos elementos en realidad fueron diseñados para principiantes
    • Leí todo con muchísimo cuidado. En efecto, mi intención real era decir que Rust era más pequeño y más limpio antes de la llegada de async y const. No lo expresé de forma directa porque tengo amigos que trabajaron en esas features. Matklad lo expresó muy bien en lobste.rs: el Rust de 2015 era más acabado y más coherente, pero la visión de Rust no es la coherencia total, sino convertirse en un lenguaje útil para la industria
  • Puede que tenga sesgo, pero creo que Rust es el lenguaje más cercano a la perfección. El borrow checker puede ser molesto, pero es necesario. Si ese mismo código con bugs estuviera en C, habría terminado en un colapso en runtime; al final igual habría que arreglar el bug. La diferencia es que Rust te obliga a resolverlo antes de compilar, mientras que en C te toca apagar incendios a medianoche. Más que difícil, Rust exige un cambio de forma de pensar. Hace falta un cambio de paradigma hacia escribir código seguro y con seguridad real. Los cambios casi siempre son incómodos, y creo que esa es la causa de fondo del rechazo que genera Rust
    • Rust está lejos de ser perfecto
      • Creo que el compilador tiene demasiada libertad para decidir cuándo y en qué orden aplicar Deref. .into() y el trait From manejan las conversiones de tipo de una forma demasiado silenciosa. Incluso en la biblioteca estándar hay muchas funciones de "conveniencia" de este tipo. Al final, el tipo real del objeto se vuelve ambiguo y cuesta más conectar una llamada de función con su implementación (aunque claro, el IDE ayuda un poco)
      • El retorno implícito (implicit return) oculta el flujo del programa y lleva a errores. El operador de interrogación tampoco me convence mucho
      • Rust divide demasiado los módulos pequeños, así que para hacer algo útil terminas necesitando cientos de dependencias. Cada una hay que mantenerla y vendorizarla por separado si quieres builds estables, y eso es realmente incómodo
      • Async Rust es un caos total en este momento
    • Más que una queja contra el borrow checker en sí, el punto principal es que el propio "bloque" de Rust se ha vuelto demasiado grande. Para quienes nos gustaba el Rust tosco e imperfecto de 2018 (me incluyo), lo de ahora ya no resulta tan atractivo. Claro, si lo dominas es muy poderoso, pero uno se pregunta si de verdad vale la pena invertir tanto esfuerzo. En 2025, como alternativa a C/C++, probablemente elegiría Zig (la única excepción sería el trabajo con Postgres, donde el ecosistema pgrx es realmente único). Aun así, cualquier cosa es mejor que trabajar en C
  • No creo que deba recomendarse Rust como primer lenguaje. Aprender el primer lenguaje ya es difícil de por sí, y con Rust, por culpa de los errores de compilación, puede ser muy complicado siquiera ver correr el programa antes de que el código esté completamente correcto. Es demasiado frustrante y mucha gente se rendiría fácilmente. Yo aconsejaría empezar con Python, JavaScript o Lua, hacer algo rápido como un juego e ir iterando
    • Mi experiencia es distinta. Un ingeniero de ML de nuestra empresa solo sabía Python, pero quería contribuir a una codebase en Rust. Le expliqué lo básico durante más o menos una hora y se adaptó enseguida; su productividad subió de inmediato. De hecho, cuando haces un juego y terminas pasando una cadena a una función numérica y todo se cae, puedes perder muchísimo tiempo rastreando la causa. En Rust, el compilador directamente te marca: "aquí hay un string pero debería haber un int", así que en cierto sentido depuras más rápido. En vez de pasarte todo el día resolviendo errores de compilación, no te pasas una semana sufriendo con errores en runtime
    • Soy la persona de la cita al inicio del blog. He enseñado Rust como primer lenguaje a más de 400 personas, y me resulta muy interesante leer las afirmaciones de este hilo. Tras mucho tiempo de experiencia directa, reuní suficiente evidencia no solo de que es posible, sino de que funciona bastante bien
    • Yo todavía no estoy convencido. Me gustaría ver a un buen educador intentando usar Rust como primer lenguaje. Las generaciones cambian y en la universidad se usa mucho Python, pero teóricamente Rust podría elevar el nivel de toda una cohorte si se usara como primer lenguaje (aunque también podría generar problemas administrativos si la tasa de reprobación fuera demasiado alta; por otro lado, los estudiantes más avanzados podrían aprender mucho más). Cosas como move assignment o el significado de la keyword const en Rust incluso podrían reducir luego el esfuerzo de desaprender malos hábitos adquiridos en lenguajes tradicionales
    • En general, yo recomendaría evitar el tipado estático como primer lenguaje. A mí me gusta, pero para principiantes suele agregar confusión innecesaria. Los errores del compilador muchas veces son contrafácticos, y mensajes del tipo "el compilador no pudo probar que esto no sea none" pueden sentirse mucho más difíciles que encontrar directamente la línea de un crash en runtime con un caso de prueba. Si vas imprimiendo valores línea por línea para hacer troubleshooting, la mayoría de las veces lo resuelves rápido, pero si te bloqueas con un error críptico del compilador puedes perder muchísimo tiempo
    • Rust no es un mal lenguaje si puedes absorber todo de una vez. El problema es que nadie aprende un lenguaje de esa manera, y si no entiendes lo suficiente los conceptos principales, en Rust vas a tropezarte una y otra vez. Y al final, como además hay muchos conceptos que ni siquiera existen en otros lenguajes, cuando te cambias a otro lenguaje puedes volver a frustrarte
  • El caso más cercano a un "Rust simple" que he visto es Gleam. Parece bastante inspirado en Rust
    • Es un malentendido decir que Gleam está inspirado en Rust. Su creador no lo dice oficialmente. El compilador está hecho en rust, pero Gleam tiene un paradigma y runtimes objetivo completamente distintos, así que no es un reemplazo de Rust
    • Si quieren otro estilo de 'simple rust', también recomendaría echarle un vistazo a fsharp
    • La página principal de Gleam tiene mensajes sobre derechos de las personas negras, derechos trans y antinazismo, así que ese lenguaje no me interesa en absoluto
    • Me pregunto si con Gleam se puede hacer 3D
  • Me molestó que "no se explica qué hace el programa Rust". Hay una explicación técnica enorme, pero no hay un resumen de qué hace realmente el programa. En realidad solo detecta cambios en archivos y los imprime. Eso muestra muy bien la dificultad del lenguaje: incluso una tarea simple en Rust se vuelve compleja de implementar, y uno tiene que preocuparse por detalles internos que no están relacionados con el problema real. Veo esa complejidad como el desafío al que te enfrentas y al mismo tiempo como una barrera autoimpuesta
    • Otros lenguajes tienen exactamente el mismo problema, pero Rust te ayuda a tratarlo por adelantado. No todos los nombres de archivo se pueden imprimir, y la mayoría de los lenguajes simplemente le trasladan ese problema al usuario. Rust deja claro en el tipo de retorno cuándo hay un error o un fallo, mientras que en otros lenguajes necesitas otro mecanismo como manejo de excepciones. Aunque a primera vista parezca más simple, en la práctica Rust podría ser incluso más intuitivo
    • De hecho, para ser un lenguaje de alto rendimiento, la implementación es muy simple. Cabe completa en una página. ¿No es eso un ejemplo suficientemente simple?
    • Explicación simple = implementación simple no siempre se cumple. XKCD 1425 lo muestra bien. (Por ejemplo: verificar si una foto fue tomada dentro de un parque nacional es fácil, pero distinguir además si es una foto de un ave ya requiere un equipo de investigación). xkcd 1425
  • Siento que Rust es bastante consistente y cohesivo semánticamente. Comparado con otros lenguajes, tiene menos azúcar sintáctica y menos cosas que actúan como azúcar, así que resulta más intuitivo. Casi todas las interfaces suelen seguir el patrón del módulo mem, así que si quieres entender bien cómo está estructurada la interfaz, conviene empezar por std::mem