12 puntos por GN⁺ 2025-07-04 | 4 comentarios | Compartir por WhatsApp
  • El proyecto tmux-rs es el trabajo de portar a Rust todo el código de tmux, originalmente escrito en C, durante unos 6 meses
  • Al principio se intentó una conversión automática con la herramienta C2Rust, pero como el resultado era difícil de mantener, al final la conversión se hizo manualmente
  • Durante el proceso de compilación y la interoperabilidad entre Rust y C, se experimentaron varios bugs y problemas estructurales
  • Hubo problemas y soluciones específicos al trasladar a Rust patrones propios de C como sentencias goto, estructuras de datos basadas en macros y parsers yacc
  • El proyecto todavía se basa en unsafe Rust, pero apunta a una migración completa a Rust mediante compilación y ejecución previas

Descripción general del proyecto

  • tmux-rs es un proyecto que portó toda la base de código de tmux (unas 67,000 líneas de código en C) a Rust (unas 81,000 líneas, sin contar comentarios ni líneas en blanco)
  • El desarrollador llevó a cabo este trabajo como un proyecto personal, y durante la migración de C a Rust pasó por muchos intentos, errores y aprendizajes

Uso de C2Rust y sus límites

  • Originalmente se intentó portar el código C de tmux a Rust usando la herramienta de conversión automática C2Rust
  • El código convertido automáticamente tenía baja legibilidad, era más de 3 veces más grande que el código C original y, por múltiples conversiones de tipos innecesarias y pérdida de nombres de constantes, la mantenibilidad se degradó mucho
  • Durante el proceso de refactorización manual, al final se descartó el resultado de C2Rust y se cambió a un método de traducir directamente a Rust tomando como referencia el código C
  • Que se pudiera compilar y ejecutar desde la etapa inicial usando C2Rust ayudó mucho a confirmar la viabilidad y factibilidad del proyecto

Diseño del proceso de compilación

  • tmux se basa en el sistema de compilación autotools, e integra el código Rust y el código C existente como bibliotecas estáticas
  • Al principio se enlazaba la biblioteca Rust con el binario C, pero una vez que la mayor parte del código se migró a Rust, se cambió a una estructura en la que el binario Rust enlaza la biblioteca C (usando el crate cc)
  • Para automatizar la compilación, se escribieron un script build.sh y un script build.rs, diseñados para permitir verificaciones graduales de compilación incluso durante la traducción
  • Problemas frecuentes del proceso de compilación, como declaraciones de headers faltantes y desajustes en las firmas de funciones, se fueron resolviendo paso a paso a nivel de función

Casos de bugs encontrados durante la traducción

Bug 1: declaración implícita y retorno de punteros

  • En una función convertida a Rust, como el tipo de retorno de puntero estaba declarado implícitamente en el código C, los 4 bytes superiores del valor de retorno se truncaban y se pasaban incorrectamente
  • La solución fue agregar en C el prototipo de función correcto para que el compilador actuara de forma adecuada

Bug 2: desajuste en el tipo de un campo de estructura

  • Debido a una traducción incorrecta del tipo de un campo en la estructura client (puntero vs entero), se desajustó el cálculo del offset de memoria, lo que provocó errores en la interpretación de datos y segfaults
  • Se resolvió haciendo coincidir exactamente la definición de la estructura en C y en Rust

Portar a Rust patrones propios de C

Uso de raw pointers

  • Mapear directamente punteros de C a referencias de Rust puede violar las reglas de seguridad de Rust, como la nulabilidad y la garantía de inicialización
  • Por eso, la mayoría de las estructuras con punteros se trasladaron como raw pointers (*mut, *const) y se usan solo dentro de zonas inseguras

Manejo de sentencias goto

  • En C2Rust, el control de flujo de goto se convierte algorítmicamente, pero en la mayoría de los casos puede implementarse suficientemente con labeled block + break y labeled loop + continue de Rust

Migración de estructuras de datos con macros

  • tmux implementa árboles rojo-negro intrusivos y listas enlazadas en macros de C
  • En Rust, se implementó una interfaz similar usando Generic trait e iteradores personalizados (el problema de implementaciones duplicadas de un único trait se resolvió con tipos dummy)

Conversión del parser yacc

  • tmux usa yacc(lex) para el parser del archivo de configuración
  • En Rust, se usó el crate lalrpop, de estructura similar, para portar tal cual la gramática y las acciones, y también se escribió un adaptador para integrarlo con el lexer en C
  • En este proceso también se encontraron límites del soporte de raw pointers de lalrpop (como el uso de NonNull<T>)

Entorno y herramientas de desarrollo

  • Principalmente se usó neovim con macros personalizadas para realizar tareas de conversión repetitivas
  • Ejemplos: ptr == NULLptr.is_null() / ptr->field(*ptr).field, entre otros mapeos hechos a mano
  • También se probaron herramientas de automatización (Cursor), pero como había mucho código perdido o incorrecto, aumentó la carga de revisión de código
  • Ayudó en cierta medida a reducir la fatiga en los dedos, pero desde el punto de vista de la productividad fue limitado

Conclusión y dirección futura

  • Todo el código ya fue migrado por completo a Rust y se publicó la versión 0.0.1
  • En comparación con C2Rust, el código manual está algo mejor en algunos aspectos, pero sigue basándose en unsafe Rust y todavía existen muchos bugs
  • El objetivo final es pasar a código safe Rust y completar la migración total de las funcionalidades de tmux a Rust
  • Se espera colaboración y feedback de desarrolladores interesados en Rust y tmux a través de GitHub Discussions

4 comentarios

 
brainer 2025-07-04

Oh... pero entonces, ¿Rust es más liviano?

 
bus710 2025-07-04

Oh... ¿suena bien, no?
Había dejado fuera resurrect de los plugins de tmux porque consume bastante memoria y a veces funciona medio raro, así que me da curiosidad si con tmux-rs será mejor.

 
GN⁺ 2025-07-04
Opiniones de Hacker News
  • De verdad me impresionó mucho la experiencia de leer el registro de este proyecto. Quiero expresar lo conmovido que me dejó. Tengo un gran respeto por la constancia y la tenacidad del autor. La frase "es como la jardinería, pero con más segfaults" me llegó profundamente. Siento que es precisamente en este tipo de proyectos de hobby serios donde más se aprende.
    La parte sobre c2rust me pareció especialmente interesante. Antes ya había visto cambios parecidos provocados por convertidores automáticos de código entre lenguajes. Estas herramientas son muy útiles para arrancar rápido un proyecto y demostrar viabilidad, pero al final suelen producir código que se siente vacío y poco natural en el lenguaje destino. Por eso creo que fue totalmente correcta la decisión de cambiar, aunque doliera, a un port manual. La automatización tiene límites cuando se trata de traducir la intención del código en C a código seguro y verdaderamente rustacean.
    En la sección de "bugs interesantes", al ver el #2 sobre struct layout mismatch, me vinieron recuerdos de antiguas pesadillas con interfaces de funciones externas (FFI). Una vez yo también perdí una semana detectando una corrupción sutil de datos por un empaquetado incorrecto de structs entre C++ y C#. Son bugs que te rompen la cabeza a nivel conceptual y de verdad te hacen dudar de tu propia cordura. Encontrar algo así requiere una paciencia enorme para depurar. Mis aplausos para el autor.
    En general, creo que este proyecto muestra muy bien la dificultad real y el proceso de modernizar código de infraestructura central. Dicen que el siguiente gran objetivo es pasar de unsafe a Safe Rust, y tengo muchísima curiosidad por saber qué estrategia seguirán.
    Sinceramente creo que reescribir todo el flujo de control complejo con raw pointers, goto, etc., a Rust idiomático y seguro sin que se desmorone todo el código puede ser incluso más difícil que el port inicial. Me pregunto si planean introducir lifetimes y el borrow checker de forma gradual por módulos, y cómo piensan manejar las estructuras de datos intrusivas. Si las reemplazan por algo como BTreeMap de la librería estándar, podría haber impacto en rendimiento; me hace pensar si el diseño intrusivo original no apuntaba justamente a evitar eso.
    En fin, es un trabajo impresionante. Gracias por compartir el proceso con tanto detalle. Voy a seguir el proyecto en GitHub.

  • Esta noticia nueva realmente me está jalando
    Desde hace unos años estoy desarrollando por mi cuenta un gestor de sesiones de tmux en Rust llamado rmuxinator (algo así como un clon de tmuxinator). La mayor parte funciona bien, pero la vida se puso ocupada y el avance iba lento; últimamente lo retomé más enfocado en corregir bugs. La función que agregué hace poco fue permitir usar rmuxinator también como librería. Me gustaría hacer una prueba para ver si funcionaría forquear tmux-rs, agregar rmuxinator como dependencia e iniciar sesiones con archivos de configuración por proyecto. No estoy diciendo que rmuxinator deba incluirse upstream, pero sí pienso que una función así de plantillas de sesión habría sido realmente útil si hubiera estado integrada directamente en el terminal multiplexer.
    A la inversa, también he pensado que quizá sería mejor que rmuxinator usara tmux-rs como librería y resolviera toda la gestión de sesiones sin generar comandos de shell (aunque todavía no sé si tmux-rs soporta eso).
    Cuando termine los bugs que estoy corrigiendo ahora, seguro voy a probar al menos una de esas ideas.
    De cualquier forma, reconocimiento para el gran trabajo de richardscollin.

  • Me encanta esa actitud de "no hay una razón especialmente buena para reescribir tmux en Rust; solo es un proyecto de hobby. Es como la jardinería, pero con más segfaults".
    No todo lo nuevo tiene que tener necesariamente una gran justificación o utilidad práctica. En los proyectos de hobby también pueden surgir descubrimientos inesperados. Me impresionó mucho lo detallado del texto del autor.
    Por cierto, en mi jardín hay muchísimos segfaults. Programar un proyecto nuevo se siente más seguro que mi patio.

    • Totalmente de acuerdo. No todos los proyectos tienen que existir para cambiar el mundo.
      Hace poco reimplementé fzf en Rust rs-fzf-clone
      No había una razón especial; fzf ya funcionaba muy bien, y el objetivo principal era experimentar de primera mano con los canales de Rust y con algoritmos de búsqueda difusa. Fue un proceso de aprendizaje muy divertido, y aunque el fzf original es mejor, eso no era necesariamente lo importante. El objetivo era probar y experimentar con algo nuevo.

    • "La jardinería es la mejor excusa para volverse filósofo"

      • Ray Bradbury, Dandelion Wine
    • Cuando alguien insinúa que Rust es automáticamente superior a C, mi reacción instintiva suele ser el cinismo. Pero a menudo se me olvida que la gente también hace estos proyectos simplemente por diversión.

    • Me quedó muy grabada la frase "no siempre necesitamos una razón solo para hacer algo nuevo".
      Aunque, en realidad, tmux no es algo nuevo.
      Eso me hace pensar si también hace falta una razón para reescribir software existente en otro lenguaje.

    • Me dio risa lo de "es como la jardinería, pero con más segfaults". Aún no estoy familiarizado con Rust, así que me da curiosidad saber en qué situaciones concretas se necesita unsafe.

  • Me impresionan mucho tanto la actitud detrás de este proyecto como el ambiente positivo de la mayoría de los comentarios.
    Siempre se dice que reescribir una aplicación madura en otro lenguaje no suele ser buena idea, pero al intentarlo se aprende muchísimo. El proceso de verdad importa más que el resultado.
    Con la atención que recibió esto y la tendencia actual del avance de la IA, creo que podría convertirse en un proyecto de hobby muy atractivo para gente que está empezando con Rust. Corregir bugs sencillos, agregar funciones nuevas u optimizar cosas sería una gran experiencia.
    Como idea, me gustaría proponer una función donde Gemini CLI (o el LLM que prefieras) actúe como una especie de scratch buffer e interactúe con distintas ventanas/paneles de una sesión de tmux.
    En mi caso, ejecuto comandos en paneles sincronizados sobre varios servidores y luego voy manejando manualmente fallos y demás; si una IA pudiera encargarse de ejecutar los comandos, analizar la salida en tiempo real y regenerar comandos de manera adaptativa, se sentiría como una especie de script de shell personalizado generado dinámicamente.

    • Respeto totalmente cualquier proyecto de hobby, sea lo que sea y como sea que se haga. Pero no logro entender por qué a alguien le resultaría interesante portar software existente tal cual de un lenguaje a otro.
      Por ejemplo, uso gvim todos los días, pero si quisiera crear un editor, no sentiría la necesidad de hacerlo como gvim; preferiría crear algo nuevo y creativo con solo las funciones que yo quiero. Si voy a invertir tanto tiempo, me parecería más valioso intentar algo creativamente único.
  • Acabo de portar tmux a Fil-C en menos de una hora (incluyendo portar libevent y pasar las pruebas). Funciona muy bien y la experiencia fue de seguridad de memoria total.

  • Me gustan este tipo de proyectos. A mí también me dan ganas de clavarme en Rust.
    De paso, quisiera mencionar zellij (un terminal multiplexer basado en Rust).
    Solo soy usuario, pero me gusta seguir buscando y migrando hacia soluciones basadas en Rust.

  • Justo estaba viendo este video, "Oxidise Your Command Line"
    https://www.youtube.com/watch?v=rWMQ-g2QDsI
    Algunas partes quizá no sean necesarias si no eres desarrollador de Rust, pero también tiene varios tips bastante útiles para cualquiera que esté familiarizado con el entorno de línea de comandos.

  • Creo que sería totalmente posible mejorar c2rust para reducir la pérdida de información que señaló el autor, como conservar nombres de constantes y cosas así. La carga del primer paso de conversión es muy grande.

    • De verdad creo que C2Rust necesita esta función sí o sí. Según entiendo, el objetivo principal de esta herramienta es generar una base de código que luego se pueda portar a Rust idiomático. Pero si se pierden por completo cosas como las definiciones de constantes, el golpe a la productividad es serio.
  • Si llega una época en la que un modelo grande de lenguaje pueda convertir automáticamente, de forma correcta y en una hora, todo un código C a Safe Rust, este tipo de proyecto probablemente se verá como un caso emblemático muy adelantado a su tiempo.
    Aun así, como el autor también comentó que lo intentó con Cursor en la etapa final (a mediados de 2025) y la eficiencia de la conversión bajó claramente, me parece que el rendimiento real todavía está lejos de eso.

    • En lugares como codemod.com ya están intentando algo así bajo el concepto de "codemods".
      Los codemods usan AST (árbol de sintaxis abstracta) para permitir conversiones y refactorizaciones de código rápidas y a gran escala.
      Introducción de Martin Fowler al refactoring de APIs con codemods

    • La parte de "poder convertir perfectamente código C complejo a Safe Rust en una hora con un modelo grande de lenguaje" suena tan específica que me resultó llamativa.

  • Espero que el código se vaya volviendo más limpio en el futuro. He probado zellij varias veces, pero incluso después de varios años de desarrollo todavía le faltan algunas funciones que tmux ofrece de manera muy cómoda.
    En particular, me molesta mucho que no se pueda ocultar/mostrar la barra de estado.
    Ver zellij-org/zellij issue #694

    • Para mí no sirve porque no se pueden mapear atajos de teclado al plugin del gestor de sesiones.
      Los atajos que uso con frecuencia chocan con los bindings predeterminados del plugin del gestor de sesiones, así que terminan bloqueando funciones clave como seleccionar directorios.
      Al final, la creación de sesiones también queda forzada a hacerse directamente desde la línea de comandos en vez de desde el plugin.