3 puntos por GN⁺ 2023-10-26 | 1 comentarios | Compartir por WhatsApp
  • Después de pasar de JavaScript a Rust para enfocarse en WebAssembly, durante 3 años evaluó el valor real de Rust creando Wick, despliegues en producción, un ebook y alrededor de 100 paquetes en crates.io
  • El borrow checker, el rico sistema de tipos, los patrones funcionales y la ausencia de null evitan muchos errores en tiempo de compilación y permiten mantener grandes bases de código con pocas pruebas
  • Clippy y Cargo workspace son potentes, pero las brechas en las herramientas y el ecosistema, como la configuración global de lint y el despliegue de workspaces, terminan convirtiéndose en costos operativos
  • async, el refactoring y la gestión de generic·lifetime·trait constraints siguen siendo áreas con más fricción que en JavaScript o Go
  • Rust es sólido y versátil, pero el costo de contratación, aprendizaje, iteración rápida y rastreo de problemas es alto, así que encaja mejor cuando el alcance está claro o se puede absorber el costo inicial

WebAssembly impulsó la elección de Rust

  • Hace algunos años dejó de lado su trabajo anterior para enfocarse 100% en WebAssembly, y en ese momento Rust era el lenguaje con mejor soporte para compilar a WebAssembly
  • Los runtimes de WebAssembly con más funcionalidades también estaban basados en Rust, así que entre las opciones disponibles Rust era la alternativa más realista
  • Después creó Wick, un framework de aplicaciones y runtime que usa WebAssembly como sistema central de módulos
  • A lo largo de 3 años acumuló experiencia con Rust pasando por varios despliegues en producción, un ebook y la publicación de alrededor de 100 paquetes en crates.io

Mantener más código con menos pruebas

  • En Rust empezó escribiendo pruebas como en cualquier otro lenguaje, pero luego descubrió que estaba escribiendo pruebas que no podían fallar si el código compilaba
  • Si se evitan bloques unsafe {} y métodos propensos a panic como .unwrap(), muchos problemas se esquivan de forma predeterminada
  • El borrow checker, el rico sistema de tipos, los patrones y bibliotecas funcionales, y la ausencia de valores null reducen el esfuerzo dedicado a las pruebas
  • En el proyecto Wick mantuvo más de 70,000 líneas de código con muchas menos pruebas de las que habría necesitado en otros lenguajes
  • Cuando sí hacen falta pruebas, el harness de pruebas de integración de Rust permite añadirlas fácilmente junto al código

Rust también cambió los hábitos de programación en otros lenguajes

  • El compilador de Rust seguía quejándose de código que en otros lenguajes se consideraba normal, y ese proceso terminó cambiando sus hábitos de programación
  • Ahora incluso en otros lenguajes le incomoda cuando el orden de las líneas se siente extraño o no se revisan los valores de retorno
  • También siente un rechazo mucho más fuerte que antes ante los errores en tiempo de ejecución
  • La rigurosidad de Rust es incómoda, pero una vez que uno se acostumbra a la protección del compilador, se vuelve difícil regresar a otros lenguajes

Clippy es útil para mucho más que linting

  • Clippy es el linter de Rust, pero está más cerca de ser una herramienta de apoyo amigable que de un simple verificador, porque sugiere código alternativo
  • La biblioteca estándar de Rust es muy grande, y con tipos, traits, macros y funciones repartidos en muchas partes, puede ser difícil encontrar la API que se necesita
  • Varias reglas detectan patrones comunes que pueden reemplazarse mejor con métodos o tipos de la biblioteca estándar
  • Cientos de reglas cubren rendimiento, legibilidad e indirección innecesaria, y cuando es posible también ofrecen código alternativo
  • La configuración global de lint a nivel de proyecto parecía llegar a través de un issue de Cargo, pero hasta entonces Wick tuvo que actualizar automáticamente con scripts la configuración inline de lint en decenas de crates

El ecosistema tiene vacíos que hay que asumir

  • El problema de la configuración global de Clippy es solo un ejemplo de los vacíos del ecosistema que aparecen con frecuencia en las herramientas y bibliotecas de Rust
  • Los issues relacionados ya están cerrados, pero permanecieron abiertos durante años y tardaron mucho en resolverse
  • Rust sigue atrayendo nuevos usuarios hasta ser elegido durante mucho tiempo como “el lenguaje más amado”, pero ese flujo no se tradujo de inmediato en mejoras drásticas en bibliotecas y herramientas
  • Era común que aparecieran forks puntuales para cubrir casos de uso específicos, y en Wick también intentaron enviar PRs pero se toparon con situaciones parecidas
  • Entre las posibles razones están la presión por mantener APIs estables y un sistema de tipos muy detallado
    • A los dueños de bibliotecas les cuesta aceptar incluso cambios pequeños porque pueden terminar implicando un cambio de versión mayor
    • También es alta la carga de escribir código Rust que satisfaga las necesidades de todo el mundo

Cargo, crates.io y la fricción al publicar workspaces

  • La estructura del repositorio de Wick se diseñó tomando como referencia proyectos populares y al principio parecía razonable, pero los problemas aparecieron en la fase de publicación
  • Con Cargo es fácil compilar, probar y usar crates del tamaño de un módulo, pero publicar en crates.io es otro asunto
  • En crates.io, todos los crates referenciados deben estar publicados individualmente para poder hacer publish de un paquete
    • Tiene sentido impedir la publicación de un crate que depende de paquetes que solo existen en el sistema de archivos local
    • Pero en una estructura natural donde un proyecto grande se divide en pequeños módulos internos, no se pueden publicar sub-crates que solo existen dentro del crate padre incluyéndolos como parte de este
  • También hubo una corrección: un crate con local dev dependency puede publicarse si incluye version en Cargo.toml
  • El soporte de Cargo para workspaces en sí es excelente, y la experiencia de gestionar proyectos grandes es mejor que en la mayoría de los lenguajes
  • Sin embargo, el workspace no resuelve el problema de publicación, y aunque existan varias formas de configurarlo, no es fácil encontrar una “respuesta correcta” que publique sin fricción
  • El hecho de que existan muchos crates utilitarios relacionados con cargo workspace publish ya muestra el problema
  • Al publicar Wick, a menudo tardaban más de 1 hora combinando tareas manuales repetitivas con herramientas que solo funcionaban parcialmente

async es una de las mayores fuentes de fricción

  • El async de Rust se siente como una función añadida después de que el lenguaje ya estaba creado, y en el uso real también suele estorbar como algo incorporado tarde
  • Es difícil entender y resolver los errores, y cuando se busca una solución también hay que filtrar entre varios runtimes y las distintas formas de async que usa cada uno
  • Algunas bibliotecas async quizá no puedan usarse fuera de un runtime async específico
  • Desde la perspectiva de alguien con 20 años usando JavaScript y experiencia con Go, el async de Rust ha sido la mayor fuente de frustración y fricción
  • No es un problema imposible de superar, pero siempre hay que estar preparado para que aparezcan problemas de async en cualquier momento
  • En otros lenguajes, async suele funcionar de manera tan natural que casi ni se nota

El refactoring puede convertirse en trabajo pesado

  • El rico sistema de tipos de Rust es a la vez una ventaja y una desventaja
  • Pensar con tipos de Rust es bueno, pero gestionar los tipos de Rust puede volverse una pesadilla
  • Los datos y las firmas de funciones pueden incluir generic type, generic lifetime y trait constraint
  • Incluso los propios constraints pueden volver a tener generic type y lifetime, así que a veces hay más type constraints que código real
  • Hay que definir los generics en cada impl, así que ya desde la primera escritura resulta engorroso, y en el refactoring un cambio pequeño puede crecer en cascada
  • Cuando hay que repetir la misma lista de constraints o generics en varios lugares, no existe una forma a nivel de lenguaje o herramientas para convertirla en alias o referenciar una definición central, así que queda la carga de la duplicación

Veredicto final: poderoso, pero costoso

  • Rust es lo bastante versátil como para escribir código a nivel de sistemas, apps CLI, servidores web y clientes web en un mismo lenguaje
  • Con WebAssembly, el mismo binario puede ejecutar un LLM en el navegador y en la línea de comandos
  • Los programas en Rust pueden ser muy sólidos, y cuando uno experimenta los problemas que Rust evita, se vuelve difícil regresar a otros lenguajes
  • Cuando volvió por un tiempo a Go, la velocidad de desarrollo volvió a ser atractiva, pero tras sufrir un panic en tiempo de ejecución, esa ventaja perdió fuerza
  • Rust sí tiene desventajas claras
    • Es difícil contratar
    • El aprendizaje es lento
    • Es demasiado rígido para la iteración rápida
    • Especialmente en código async, es difícil rastrear problemas de memoria y rendimiento
    • No todas las bibliotecas son lo bastante buenas para código seguro
    • Las herramientas de desarrollo todavía tienen mucho margen de mejora
  • Aunque con un equipo pequeño lograron cosas sorprendentes, también hubo grandes obstáculos, y como además existían razones técnicas por las que Rust encajaba mejor, aún considera pronto decidir si Rust realmente valió la pena para Wick
  • Si se necesita iterar rápido, Rust probablemente no sea una buena opción
  • Si el alcance ya se conoce o se puede asumir un costo inicial más alto, vale la pena considerar Rust seriamente
  • A medida que la perspectiva de WebAssembly gana fuerza mes a mes, la posibilidad de reutilizar software sólido escrito una sola vez en muchos lugares se siente cada vez más cercana a la realidad

1 comentarios

 
GN⁺ 2023-10-26
Comentarios de Hacker News
  • He usado mucho Rust, pero incluso después de varios años sigo sintiendo que la productividad es baja
    Últimamente uso mucho Zig, y siento que soy como 10 veces más productivo porque puedo concentrarme solo en el código que quiero escribir y no tengo que pensar qué herramienta o biblioteca debería usar
    Entiendo que Rust ofrece seguridad de memoria y que eso importa, pero su usabilidad es realmente mala. Cada vez que uso Rust siento que estoy restringido, y como siempre tengo que buscar bibliotecas o investigar cómo hacer algo, no puedo simplemente “ponerme a escribir código”
    El sistema de tipos también puede crecer hasta volverse inmanejable, y muchas veces es difícil saber qué métodos se pueden invocar realmente en cierta estructura. Rust es una herramienta excelente y resuelve muchos problemas, pero no creo que sea un buen lenguaje de propósito general

    • Esa sensación no es común para todo el mundo. Para cualquier tarea un poco más compleja que un script de shell, uso Rust, y hasta controlo mi gestor de ventanas con programas en Rust
      Incluso hablando desde alguien que ha usado Python por casi 20 años, ahora trabajo en Rust tan rápido como en Python
    • Estoy de acuerdo. A estas alturas siento que soy mucho más productivo en C y C++ que en Rust
      Para mí, Rust falla por completo en encontrar el equilibrio adecuado. Es demasiado exigente con detalles de bajo nivel para escribir aplicaciones de alto nivel, y demasiado complejo para trabajar en embebidos o sistemas operativos
      Para lo primero elegiría C++, Java, Haskell, OCaml, o incluso Go mezclado con un poco de C; para lo segundo, C usado casi como macroensamblador encaja mucho mejor
      Sigo sintiendo que la idea original de Graydon Hoare —algo más del lado de OCaml/SML con tipos lineales, recolección de basura, asignación en stack, green threads y CPS— habría sido un lenguaje mucho mejor
    • Mi experiencia es exactamente la contraria. Usando Rust en sistemas embebidos, mi confianza y mi velocidad mejoraron muchísimo
      En C, un pequeño error suele terminar en comportamiento indefinido y dolores de cabeza, pero en Rust eso no pasa, y para mí cambió por completo las reglas del juego
    • Me da curiosidad qué tipo de código escribes. ¿Muy de bajo nivel o muy de alto nivel?
      También me pregunto si con eso quieres decir que en Zig no hace falta buscar bibliotecas ni averiguar cómo hacer las cosas
    • Coincido con la sensación general, aunque me cuesta expresarla con palabras
      Rust te obliga a decidir de antemano adónde va cada bit y byte, en qué hilo se usa y bajo qué forma de mutación se va a manejar. A menos que estés haciendo parsers o cosas a nivel de microcontrolador, ese proceso se siente tedioso
      Me gusta primero hacer que algo funcione y después decidir cuál es la mejor estructura de API, y Rust choca con ese proceso
      Aunque el sistema de tipos de Rust es más potente, con Swift también puedes obtener el 90% del rendimiento y el flujo se siente mucho más natural
  • La mayor crítica podría ser que crates.io no tiene espacios de nombres
    Cualquiera puede apartar nombres de paquetes globales y genéricos, y a menos que evites el repositorio de crates.io, la mayoría tiene que aguantarse con eso. Pero algunos de esos paquetes con nombres genéricos ya apartados ni siquiera son los mejores para usar en la práctica
    Tal vez fue una reacción contra la notación de DNS invertido al estilo Java por ser larga y molesta, pero algo como poner un espacio de nombres de usuario/grupo delante del nombre del paquete, al estilo GitHub, habría sido un buen punto medio

    • Hice un análisis para encontrar acaparadores de nombres en crates.io, y el mayor acaparador resultó crear aproximadamente un crate cada 30 segundos, durante toda la semana
      Envié ese análisis al equipo de crates.io y también señalé que existe una política contra la automatización, pero respondieron que no era evidencia suficiente de acaparamiento de nombres
      El problema con crates.io es que, aunque haya políticas claras, no las hacen cumplir. Por eso, todos los nombres cortos y fáciles de recordar ya están tomados, y no hay forma de recuperarlos
    • Más que una reacción contra el DNS invertido al estilo Java, parece que siguieron la convención establecida por la mayoría de los gestores de paquetes de ese momento
      No recuerdo muchos gestores de paquetes no relacionados con Java en 2014~2015, cuando apareció Rust, que no usaran un único espacio de nombres global: NPM, PyPI, RubyGems, Hex de Elixir, Cabal de Haskell, etc.
      Algunos intentaron corregir eso después, pero en ese entonces así era como funcionaban los gestores de paquetes
    • Maven y Java no reciben suficiente reconocimiento por lo bien que funciona su gestión de dependencias
      Los peores sistemas de gestión de dependencias de otros lenguajes que vinieron después casi no aprendieron nada de esos casos anteriores
    • Usar URL para los paquetes tiene bastante sentido. Funciona bien en el ecosistema de Go
      Además tiene la ventaja de que ya no hace falta una base de datos global de paquetes a nivel de lenguaje. Subes un paquete a example.com/your-thing y queda liberado de inmediato
      Claro, si quieres, cachés y motores de búsqueda se pueden ofrecer por separado
    • No uso Rust muy seguido, pero este tipo de problema es realmente molesto en todos los repositorios de paquetes
      Es como eso de que http-server es malo y en cambio deberías usar MuffinTop, pero simplemente tienes que saberlo
      La idea de un nombre de paquete oficial es interesante, pero si con el tiempo cambia el código detrás del alias, en realidad es muy probable que termine causando confusión
      Al final, parece que esto seguirá siendo parte del proceso de volverse experto en un ecosistema
  • Si creas .cargo/config.toml en la raíz del workspace, se aplica a todos los crates, así que puedes configurar lints globales de Clippy
    Dentro del archivo, bajo [build], basta con poner algo como rustflags = ["-Wclippy::lint_name_to_warn", "-Dclippy::lint_name_to_deny"]
    Eso sí, rustflags no se agrega sino que sobrescribe, así que si hay otra fuente como la variable de entorno RUSTFLAGS, esa configuración quedará reemplazada

    • Nosotros ejecutamos al hacer commit un script que agrega lints en los archivos lib.rs o main.rs. Es simple
  • Estoy aprendiendo Rust porque parece claro que va a volverse importante profesionalmente. De verdad quiero que me guste y le veo ventajas, pero entra en la categoría de los lenguajes más desagradables que he usado hasta ahora
    Seguí esperando que, al ganar soltura, se me pasara ese rechazo, pero mientras más avanzo por la curva de aprendizaje, no es que le agarre cariño
    Está bien. Seguro no será el único lenguaje que maneje bien y aun así no me guste. Solo que hay tanta gente diciendo que ama Rust que pensé que yo también lo disfrutaría

    • Como contraejemplo, a mí sí me gusta programar en Rust. La etapa de pelearme con el borrow checker quedó atrás hace mucho, y hoy en día casi ni me da errores, salvo cuando los provoco a propósito para verificar tipos
      También da la impresión de que el compilador de Rust ha mejorado para aceptar casos válidos más amplios
      La clave para entender el borrow checker fue entender el modelo de memoria subyacente. El modelo de memoria de Rust es igual al de C, salvo por extensiones para abstracciones como genéricos
      Las reglas del borrow checker al principio parecen arbitrarias, pero están profundamente conectadas con ese modelo de memoria. Cuando caigo en el borrow checker sin querer, ahí es donde realmente aporta valor, porque suele ser un bug causado por un descuido
      Lo aterrador es que, en un lenguaje como C o C++, ese código simplemente se aceptaría y seguiría adelante
      El sistema de tipos estricto de Rust y el borrow checker te empujan suavemente a estructurar bien el código, y estoy convencido de que han mejorado cómo diseño código en todos los lenguajes que uso
    • He intentado aprender Rust varias veces, pero siempre termino rebotando. Simplemente se siente poco usable
    • Algo que todavía me molesta es que no tiene valores predeterminados/argumentos con nombre en funciones
      Es una función de usabilidad muy básica para programadores, compartida por la mayoría de los lenguajes populares, e incluso C++ tiene argumentos predeterminados desde hace muchísimo
    • Me da curiosidad qué es lo que no te gusta
  • Como exprogramador de C/C++ que llamó a pthread casi todas las semanas durante 10 años, ahora uso Rust asíncrono para todo
    No entiendo por qué lo asíncrono recibe tanto odio. En mi opinión, todos deberían usar async para todo. Incluso para tareas “simples” que en apariencia son de un solo hilo

    • Yo diría que el problema es su carácter contagioso. Sobre todo en entornos embebidos o Wasm, el async dominante puede no ser el async que yo quiero
      Si el caso de uso del autor hubiera sido Wasm, seguramente tendría otra perspectiva
      Los trabajos que usan buffers grandes o los reutilizan para evitar el costo de asignación muchas veces también se benefician del viejo estilo de pool de hilos. Se puede mitigar algo con bump allocators o allocators fragmentados, pero cuando estás limitado por CPU en bucles ajustados que pueden vectorizarse, un pool de hilos suele funcionar mejor
      Async es una buena herramienta, pero no existe solo el contexto óptimo para ella
    • Hace poco probé un poco de Rust asíncrono para comparar cómo difiere el manejo de errores frente a Go en llamadas async anidadas, pero hasta un ejemplo trivial de lo que quería escribir parecía requerir Tokio
      Go, C# y TypeScript no tienen esa barrera
      Por ejemplo, parece que ni siquiera puedes usar await dentro de la función main sin un decorador importado de Tokio
    • El problema con el que me topé ahora es que algunos elementos, como Engine de rhai, no son Send, y estoy tratando de usarlos dentro de un closure async
      GPT-4 sugirió crear un tokio Runtime dentro de un hilo y usar block_on(). Voy a intentarlo mañana. Este es mi primer proyecto serio en Rust
    • Yo aprendí lo contrario. Los programadores tienden a volver todo innecesariamente asíncrono aunque no lo necesiten, aumentando la complejidad y la carga mental
    • ¿Podrías explicarlo un poco más? Me interesa qué ejemplos habría para elegir async en tareas simples que aparentan ser de un solo hilo
      También me da curiosidad si cuando dices “ahora uso Rust asíncrono para todo” quieres decir “uso Tokio para todo”
  • Programar en Rust de verdad no se parece a una relación abusiva. El compilador intenta ayudarte al máximo y, en particular, los mensajes de error de rustc están entre los mejores del mundo

    • El sistema operativo espera que el programador maneje correctamente los recursos, y el compilador de Rust hace que eso sea muy fácil
      Lo que se parece más a una relación abusiva no es el compilador de Rust, sino el sistema operativo. Lo mismo con el hardware: también hay que ejecutar correctamente el ensamblador, así que visto así podría llamarse una relación abusiva
      Los mensajes de error de Rust están en otra liga, y no hay ningún compilador que se le acerque
      Además, hace poco usé LaTeX y los mensajes de error fueron horribles. Fue una pesadilla tener que inspeccionarlos para averiguar qué estaba mal
    • En general son buenos, pero realmente odio que cualquier error dentro de una función async termine generando, en todos los puntos de llamada recursiva, errores de que el Future ya no es Send ni Sync
      Toda la consola queda inundada de errores, y el error de sintaxis real queda enterrado por ahí en medio
    • El compilador de Rust fue el primero que vi usar la palabra “perhaps”
      Mi principal queja es que todavía no entiendo por completo los lifetimes, y el compilador tampoco puede ayudarte siempre. Se entiende, porque el compilador tiene que ser conservador
  • En las pruebas de C++ se suele decir: “si compila, probablemente está bien”
    Si crees que Rust maneja tantos errores que los casos de prueba comunes se vuelven inútiles, yo lo vería como una señal de que en otros lenguajes no se estaba probando lo correcto
    Lo que hay que probar no son los problemas del lenguaje en sí, sino la lógica de negocio
    Si al ver un test piensas “en JavaScript lo probaría, pero en Rust no hace falta”, entonces simplemente hay que borrar ese test

    • He escuchado eso de “si compila, probablemente está bien” aplicado a Haskell y Rust, pero nunca a C++
    • Una excepción por puntero nulo es un bug que rompe la lógica de negocio
      No existe esa separación entre “lógica de negocio vs. problemas del lenguaje”, porque el lenguaje es la base sobre la que se sostiene la lógica de negocio
      Si no pruebas los modos de fallo, no sé cuál sería el sentido de hacer pruebas
    • Hay algo casi admirable en cierto tipo de programador de C++ que ve que un código basura roto compiló y asume que entonces probablemente está bien
      A diferencia de la mayoría de los lenguajes conocidos, C++ tiene IFNDR, y en broma se le llama un falso positivo a la pregunta “¿esto es un programa en C++?”
      A un compilador de C++ conforme al estándar se le prohíbe avisarte en algunos casos donde sospecha que el código que escribiste no tiene sentido; simplemente debe seguir adelante y emitir algo
      Ese algo puede ser un ejecutable funcional, o un ejecutable que provoque un desastre total todos los viernes. No hay forma de saberlo
      El estándar ISO sí identifica estos casos, pero de forma tan vaga que es difícil delimitar exactamente qué entra ahí, y mi impresión es que hoy en día la mayoría del software no trivial en C++ probablemente sea IFNDR. Mejor rechazar todo el lenguaje
  • Rust parece haber roto por fin la idea de que el programador debe controlar por completo y ser consciente de todo lo que hace el compilador
    En realidad eso ya no era cierto desde hace décadas, y los compiladores eran casi magia. Rust revirtió bastante esa tendencia con los préstamos, haciendo que la gente se sintiera cómoda con que el compilador sabe más que uno mismo
    Ojalá se avanzara aún más. No debería ser necesario iterar explícitamente una colección de principio a fin salvo que el algoritmo lo requiera. Varias tareas deberían paralelizarse implícitamente. Ojalá existiera un Bash al estilo Rust

    • Mi experiencia con Rust es exactamente la contraria
      Rust es bastante transparente respecto de lo que hace, y es muy conservador con la magia del compilador. El lenguaje no hace asignaciones en heap, no hace conteo de referencias y no tiene conversiones implícitas de tipos numéricos
      No copia tipos a menos que se haya declarado implícitamente que se pueden copiar, e incluso eso solo es legal para tipos que pueden copiarse con un simple memcpy superficial
      Rust usa abstracciones de costo cero por todas partes, así que suele ser predecible a qué código va a compilar, y normalmente es simple. También se conoce bien la disposición por defecto de los tipos estándar, así que se sabe que recorrer un Vec va a compilar a un bucle que incrementa punteros, y no hay paralelismo implícito
      Describir los préstamos como “el compilador sabe más que el programador” es raro. Los préstamos se parecen más a la comprobación de tipos. Si declaras algo como temporal y luego intentas usarlo como si viviera más tiempo, obtendrás un error
      Es lo mismo que declarar que una función devuelve una estructura Foo y luego devolver Bar. Si el compilador “sabe más”, es simplemente porque escribiste un bug
      Los préstamos también se compilan a uso directo de punteros sin recolección de basura, y en estructuras y funciones con ABI de C se garantiza literalmente que son iguales a punteros de C. Si crees que sabes más que el compilador, incluso puedes saltarte las vidas útiles con unsafe
    • Ya existe un crate que ofrece iteradores paralelos. Solo hay que cambiar el nombre de la llamada a iter()
      Aun así, no estoy de acuerdo con que eso deba ser implícito
    • Decir que “no puedes controlar lo que hace el compilador” es un poco exagerado
      En algunos sentidos Rust le da al programador más control que C. Por ejemplo, Rust soporta ensamblador en línea como parte estándar, mientras que el ensamblador en línea de C depende de extensiones específicas del proveedor
      Eso sí, los valores por defecto convenientes son muy distintos. En Rust, los casts inseguros de tipos requieren mucho procedimiento y cuidado, y hay que seguir más reglas que en C
      En particular, las referencias de Rust en la práctica actúan todas como restrict, y es muy fácil arruinar eso al hacer casts unsafe desde punteros sin procesar a referencias seguras. Por eso hay un fuerte incentivo para no escribir ese tipo de código si tienes otra opción
    • Como alguien que usa mucho unsafe Rust, incluyendo cosas como tagged pointers, no estoy para nada de acuerdo
      Más bien quiero un control todavía más explícito, y me gustaría reducir esa carga con un sistema de tipos más expresivo. Idealmente, me gustaría que el sistema de tipos de Rust se pareciera más a una variante de Prolog
    • Si hubiera un “Bash al estilo Rust”, sería un Bash con tipos, especialmente para coma flotante, menos situaciones excepcionales, funciones con parámetros explícitos y flags de línea de comandos simples
  • Aprender cuándo invertir en restricciones de tipos y cuándo no hacerlo es una lección importante
    No es un problema exclusivo de Rust, aunque la forma de expresarlo puede ser un poco distinta
    He trabajado con C++ sobretipado y con Java excesivamente abstraído y tipado, y ambos tienen el mismo tipo de problemas al refactorizar
    Por otro lado, también he visto mucho Go con pocos tipos y poca documentación, donde ciertos valores quedan dispersos por todas partes y se convierten en minas terrestres en tiempo de ejecución, y refactorizar puede volverse realmente muy difícil
    Al principio se siente que avanzas más rápido, pero normalmente terminas entregando bugs a los usuarios
    No hay una respuesta mágica en esta compensación. Rust ofrece un rango bastante amplio de opciones en este eje

  • La frase “Rust te grita todo el día, todos los días, por cosas que en tu vida anterior habrías considerado completamente normales” también aplica más o menos a un buen compilador de C si activas todos los flags
    Me gustan los lenguajes y compiladores donde puedes apagar opcionalmente esos gritos y te dejan escribir código malo a propósito. Muchas veces es mejor tener código malo que funciona y se puede escribir rápido que código perfecto pero que toma una eternidad
    Puedes hacer una prueba de concepto mala pero funcional, y luego corregirla para que sea menos mala
    El punto de que “atrae bien a nuevos usuarios, pero las bibliotecas o herramientas no mejoran de forma dramática, y solo aparecen forks de una sola vez para resolver casos de uso específicos” no tiene que ver con la edad
    Es difícil atraer desarrolladores clave, y hace falta mucho esfuerzo para volverlo atractivo. Además, las convenciones culturales las definen los primeros adoptantes, y no tener convenciones muchas veces es tan dañino como tener malas convenciones
    Si miras Python, por la forma relajada en que se abordaron los entornos de desarrollo y ejecución, terminaron compitiendo como 50 maneras distintas de desarrollar o ejecutar programas en Python
    PyPI, el repositorio de paquetes más usado, fue un desastre durante años; pocos construyen sobre paquetes existentes, los nombres parecen generados por un generador aleatorio de palabras, el ecosistema tiene mucho malware, y ni siquiera puedes buscar paquetes desde la línea de comandos
    No es culpa del lenguaje, sino de una comunidad y un equipo central que actuaron como espectadores. La cultura importa más que la tecnología que está en el centro
    No lo digo para criticar solo a Python; simplemente conozco mejor ese problema. C lleva medio siglo existiendo, pero su comunidad tampoco ha logrado ordenar bien ni la mitad de las soluciones que los lenguajes más modernos ya prepararon

  • Rust también permite eso. Solo hay que marcarlo todo como unsafe

  • Es poco probable que Linux y Windows hayan empezado a portar componentes a Rust por la razón de que “no hay bibliotecas mejoradas”

  • Me atrevería a decir que eso se parece mucho a TypeScript
    Me refiero a esas situaciones en las que el compilador te grita que lo arregles, pero antes de pulirlo por completo solo quieres probar una idea