Hacer que los binarios de Rust sean más pequeños por defecto

Los binarios de “hello world” de Rust han sido criticados durante mucho tiempo por medir varios megabytes, algo que muchos consideran que socava su reputación de “abstracciones de coste cero” y ahuyenta a desarrolladores acostumbrados a ejecutables C diminutos. Un cambio reciente en Cargo eliminará por defecto los símbolos de depuración de la biblioteca estándar cuando no se solicite debuginfo, reduciendo los binarios de release típicos en torno a un 90% (hasta unos pocos cientos de kilobytes) y manteniendo los símbolos completos disponibles para compilaciones de desarrollo. Los comentaristas debaten cuánto importa el tamaño del binario en sistemas modernos frente a entornos embebidos y con recursos limitados, y sopesan este cambio frente a compensaciones relacionadas con backtraces, comportamiento de panic, optimización en tiempo de enlace y el diseño de Rust con enlazado estático y sin ABI estable.

Primeras impresiones y el tamaño del binario como señal

  • Varios comentaristas dicen que un “hello world” de varios MB puede echar atrás a los evaluadores de inmediato, especialmente a desarrolladores de C/C++ acostumbrados a binarios diminutos.
  • Otros argumentan que el tamaño inicial es sobre todo un coste fijo y no predice el crecimiento marginal a medida que los programas se hacen más grandes.
  • Hay debate sobre si el “tamaño de hello world” es una buena proxy del coste de abstracción: algunos creen que sí en general, mientras que otros señalan que el salto de Rust de 4 MB a 400 KB (mediante el stripping de depuración) muestra que el tamaño puede depender mucho de decisiones de tooling, no de la sobrecarga del lenguaje.

¿Qué hay dentro de un “hello world” de Rust de ~415 KB?

  • Principales contribuyentes citados:
    • Soporte de backtrace: recorrido de pila, análisis de DWARF, desmanglado de nombres, manejo de rutas, secciones ELF comprimidas.
    • Mecanismos de E/S: stdout con buffering, sincronización, allocator, implementación de vectores.
    • Formateo, especialmente de floats, que dependen de tablas de datos considerables y del manejo de casos límite.
  • El enlazado estático de std (por portabilidad y por la falta de una ABI estable de Rust) incorpora inherentemente más que un programa C enlazado dinámicamente.

Eliminar información de depuración y trazas de pila

  • Comportamiento anterior: los binarios de release seguían incrustando la información de depuración de std, inflando el “hello world” hasta ~4 MB.
  • Nuevo valor por defecto: strip = "debuginfo" cuando no se solicita debuginfo, eliminando el DWARF de std pero preservando el comportamiento en tiempo de ejecución.
  • Consecuencia: los backtraces de release pierden números de línea, pero los comentaristas señalan que rara vez eran útiles sin información de depuración para el código del usuario.
  • Algunos temen perder símbolos; otros enfatizan que la información de depuración externa/separada y debuginfod son el patrón correcto a largo plazo.

Ajuste manual del tamaño y compensaciones

  • Receta común: opt-level="z", lto=true, codegen-units=1, panic="abort", además de compilar std con panic_abort / panic_immediate_abort y hacer stripping. Esto puede llegar a decenas de KB.
  • Compensaciones discutidas:
    • Compilación más lenta y, a veces, ejecución más lenta para compilaciones optimizadas para tamaño.
    • panic="abort" elimina el unwinding, los destructores en caso de panic y algunos patrones de recuperación; se compara con -fno-exceptions.
    • Hacer stripping de todo (no solo debuginfo) puede romper binarios; se destaca --strip-unneeded.

Cuándo importa el tamaño del binario (y cuándo no)

  • Algunos dicen que cualquier cosa por debajo de 1 MB está bien para la mayoría de usos de escritorio/servidor; el rendimiento y el tiempo de compilación importan más.
  • Otros, especialmente en embedded/Linux sobre dispositivos con recursos limitados y en contenedores con pocos recursos, subrayan que los kilobytes suman y pueden ser una restricción dura.
  • También existe un ángulo ambiental/de eficiencia: los binarios grandes y el trabajo desperdiciado se ven como hinchazón sistémica.

Enlazado estático vs dinámico y estabilidad de ABI

  • Rust usa por defecto std enlazado estáticamente porque su ABI es inestable; las bibliotecas Rust dinámicas son posibles, pero se desaconsejan.
  • Puede usarse la ABI de C para objetos compartidos estables invocables desde otros lenguajes.
  • El Rust embebido a menudo usa #![no_std], lo que evita gran parte de esta sobrecarga; pero muchos sistemas “embebidos” sobre Linux siguen preocupándose por el tamaño de los binarios con std completo.

Proceso del proyecto y actitudes

  • Algunos critican que este problema “obvio” se haya prolongado durante ~7 años, viéndolo como una señal de enfoque en funciones por encima de los fundamentos.
  • Otros lo presentan como una priorización normal: pocos usuarios estaban bloqueados, había soluciones alternativas y competían por atención muchos problemas más urgentes.
  • Hay amplio acuerdo en que mejorar los valores por defecto, incluso para problemas con soluciones conocidas, es importante para el pulido y la percepción de Rust.