- 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 == NULL → ptr.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
Oh... pero entonces, ¿Rust es más liviano?
Oh... ¿suena bien, no?
Había dejado fuera
resurrectde 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.https://rosettalens.com/s/ko/introduccion-a-tmux-rs
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
c2rustme 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 destructsentre 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
unsafea 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 introducirlifetimesy elborrow checkerde forma gradual por módulos, y cómo piensan manejar las estructuras de datos intrusivas. Si las reemplazan por algo comoBTreeMapde 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
tmuxen Rust llamadormuxinator(algo así como un clon detmuxinator). 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 usarrmuxinatortambién como librería. Me gustaría hacer una prueba para ver si funcionaría forqueartmux-rs, agregarrmuxinatorcomo dependencia e iniciar sesiones con archivos de configuración por proyecto. No estoy diciendo quermuxinatordeba 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
rmuxinatorusaratmux-rscomo librería y resolviera toda la gestión de sesiones sin generar comandos de shell (aunque todavía no sé sitmux-rssoporta 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
tmuxen Rust; solo es un proyecto de hobby. Es como la jardinería, pero con mássegfaults".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é
fzfen Rust rs-fzf-cloneNo había una razón especial;
fzfya 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 elfzforiginal 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"
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,
tmuxno 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 necesitaunsafe.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 buffere interactúe con distintas ventanas/paneles de una sesión detmux.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.
Por ejemplo, uso
gvimtodos los días, pero si quisiera crear un editor, no sentiría la necesidad de hacerlo comogvim; 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
tmuxa Fil-C en menos de una hora (incluyendo portarlibeventy 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
c2rustpara 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.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
codemodsusan 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
zellijvarias veces, pero incluso después de varios años de desarrollo todavía le faltan algunas funciones quetmuxofrece de manera muy cómoda.En particular, me molesta mucho que no se pueda ocultar/mostrar la barra de estado.
Ver
zellij-org/zellijissue #694Los 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.