7P by GN⁺ 12시간전 | ★ favorite | 댓글 4개
  • tmux-rs 프로젝트는 약 6개월간 C로 작성된 tmux 전체 코드를 Rust로 포팅한 작업임
  • C2Rust 툴을 사용해 초기에 자동 변환을 시도했지만, 결과물의 유지보수성이 낮아져 결국 수작업으로 변환을 진행함
  • 빌드 과정과 Rust-C 상호 연동에서 여러 버그와 구조적 문제를 경험하였음
  • goto 문, 매크로 데이터구조, yacc 파서 등 C 고유 패턴을 Rust로 옮기는 특별한 이슈와 해법이 있었음
  • 프로젝트는 아직 unsafe Rust 기반이지만, 사전 컴파일·런 실행을 통해 Rust로의 완전 이식을 목표로 함

프로젝트 개요

  • tmux-rs는 tmux의 전체 코드베이스(약 67,000줄의 C 코드)Rust(약 81,000줄, 주석·빈 줄 제외) 로 포팅한 프로젝트임
  • 개발자는 이 작업을 취미 프로젝트로 진행했으며, C에서 Rust로의 이식 과정에서 많은 시행착오와 학습을 경험함

C2Rust 사용과 한계

  • 본래 C2Rust라는 자동 변환 툴을 사용하여 tmux C 코드를 Rust로 이식하려고 했음
  • 자동 변환 코드는 가독성이 떨어지고, 원본 C 코드 대비 3배 이상 커졌으며, 각종 불필요한 형 변환, 상수명 손실 등으로 인해 유지보수성이 크게 저하됨
  • 수작업 리팩토링 과정에서 결국 C2Rust 결과물을 폐기하고, C 코드를 참조해 직접 Rust로 옮기는 방식으로 전환함
  • C2Rust를 사용해 초기 단계에서도 빌드·실행이 가능했던 점은 프로젝트의 타당성과 실현 가능성을 확인하는데 큰 도움이었음

빌드 프로세스 설계

  • tmux는 autotools 빌드 시스템을 기반으로 하며, Rust 코드와 기존 C 코드를 정적 라이브러리로 연동
  • 초기에는 Rust 라이브러리를 C 바이너리에 링크하는 식이었으나, 코드가 대부분 Rust로 이식된 이후부터는 Rust 바이너리에서 C 라이브러리를 링크하는 구조로 변경함(cc crate 사용)
  • 빌드 자동화를 위해 build.sh 스크립트build.rs 스크립트를 작성해 번역 중에도 점진적으로 빌드 검사 가능하게 설계함
  • 헤더 선언 누락, 함수 시그니처 불일치 등 빌드 과정에서 자주 발생하는 문제를 함수 단위로 단계적으로 해결함

번역 중 겪은 버그 사례

버그 1: 암시적 선언과 포인터 반환

  • Rust로 변환한 함수에서 포인터 반환형이 C 코드에 암시적 선언된 탓에 return값의 상위 4바이트가 잘려서 잘못 전달되는 문제가 발생함
  • 해결책은 C 쪽에 정확한 함수 프로토타입을 추가해 컴파일러가 올바른 동작을 하게 하는 것임

버그 2: 구조체 필드 타입 불일치

  • client 구조체에서 필드 타입(포인터 vs 정수) 오역으로 인해, 메모리 오프셋 계산이 어긋나 데이터 해석 오류와 segfault가 발생함
  • 정확한 구조체 정의를 C와 Rust에서 일치시키는 것으로 해결함

C 고유 패턴을 Rust로 이식

Raw pointer 활용

  • C 포인터를 Rust 레퍼런스로 직접 매핑하는 것은 null 허용성, 초기화 보장 등 Rust의 안전성 규칙에 위배될 수 있음
  • 따라서, 대부분의 포인터 구조는 raw pointer (*mut, *const) 로 옮겨, 안전하지 않은 영역에서만 사용함

goto 문 처리

  • C2Rust에서는 goto의 흐름 제어를 알고리듬으로 변환하지만, 대부분의 경우 Rust의 labeled block + break, labeled loop + continue로 충분히 구현 가능함

매크로 데이터구조 이식

  • tmux는 침입형 red-black 트리, 링크드 리스트를 C 매크로로 구현
  • Rust에서는 Generic trait과 커스텀 iterator를 사용해 유사한 인터페이스를 구현함(단일 trait의 중복 구현 문제는 dummy 타입으로 해결)

yacc 파서 변환

  • tmux는 설정 파일 파서를 위해 yacc(lex) 를 사용
  • Rust에서는 구조가 유사한 lalrpop crate를 사용해 문법과 액션을 그대로 포팅하고, C lexer와의 연동용 어댑터도 작성
  • 이 과정에서 lalrpop의 raw pointer 지원 한계(NonNull<T> 활용) 등도 경험함

개발 환경 및 도구

  • 주로 neovim에 커스텀 매크로를 활용하여 반복적 변환 작업을 수행
  • 예: ptr == NULLptr.is_null() / ptr->field(*ptr).field 등 손수 매핑
  • 자동화 도구(Cursor)도 시도했으나, 손실되거나 잘못된 코드가 많아 코드 리뷰 부담이 커짐
  • 손가락 피로를 줄이는 데 일부 도움됐으나, 생산성 측면에선 제한적임

결론 및 향후 방향

  • 전체 코드는 완전히 Rust로 이식 완료/버전 0.0.1 공개
  • C2Rust 대비 수작업 코드가 일부 더 나은 수준이나, 여전히 unsafe Rust 기반, 다수의 버그가 존재
  • 최종 목표는 safe Rust 코드로의 전환 및 tmux 기능의 완전한 Rust 이식 완성
  • Rust, tmux에 관심 있는 개발자와의 협업 및 피드백을 GitHub Discussions를 통해 희망함

오.. 근데 Rust가 더 가벼운가요?

오... 좋은데요?
tmux 플러그인 중에서 resurect가 은근히 메모리 많이 먹고 이상허게 동작하는 것도 있어서 빼놓았는데, tmux-rs와 함께라면 더 나을지 궁금허네요.

Hacker News 의견
  • 정말 대단한 프로젝트 기록물 읽기 경험이었음에 감동 느낌 전달하고 싶음. 저자의 꾸준함과 집요함에 큰 존경심 표시하고 싶음. "정원 가꾸기와 비슷하지만 segfault가 더 많음"이라는 표현이 깊이 공감됨. 이런 진지한 취미 프로젝트에서 가장 많이 배움이 생기는 경험임.
    c2rust 관련 경험이 특히 흥미로웠음. 예전에도 언어 간 자동 코드 변환기가 가져온 비슷한 변화를 본 적이 있음. 이런 툴로 빠르게 프로젝트를 초기화하고 실현 가능성 증명하는 데는 아주 유용하지만, 결국 타깃 언어답지 않은, 비어있는 느낌의 코드가 나오기 십상임. 결국 고통스럽더라도 수동 포팅으로 전환한 선택이 정말 옳았다고 생각함. 자동으로는 C 코드의 의도를 안전하고 러스트스러운 코드로 번역하는 데 한계가 있음.
    "재미있는 버그" 섹션에서 #2번 struct layout mismatch 얘기를 보고 과거 외부 함수 인터페이스(FFI) 악몽이 떠올랐음. 나도 한 번 C++과 C# 사이에서 struct 패킹이 틀려서 일주일을 허비하며 미묘한 데이터 손상을 잡아낸 적 있음. 의미상으로 미치는 버그라 진짜 제정신을 의심하게 만듦. 이런 걸 찾아내려면 대단한 디버깅 인내심이 필요함. 저자에게 박수를 보냄.
    전체적으로 이 프로젝트는 핵심 인프라 코드를 현대화하는 현실적인 난이도와 과정을 잘 보여주는 케이스라 생각함. 다음 큰 목표가 unsafe에서 safe Rust로 넘어가는 거라는데, 어떤 전략일지 정말 궁금함.
    raw pointer, goto 등 복잡한 제어 흐름을 모두 idiomatic하고 안전한 Rust로 고치되 코드 전체가 무너지지 않게 하는 게 사실상 처음 포팅보다 더 어려울 수 있다고 생각함. 라이프타임과 borrow checker를 모듈 단위로 점진적 도입할 계획인지, intrusive 자료구조를 어떻게 처리할 계획인지 궁금함. 표준 라이브러리의 BTreeMap 같은 걸로 대체하면 성능 영향이 있을 수 있다고 보는데, 원래 intrusive 설계가 그걸 의도한 것 아닐지 생각됨.
    아무튼 놀라운 작업임. 이렇게 상세하게 과정 공유해줘서 고마움. 깃허브에서 프로젝트 계속 팔로우할 예정임

  • 이번 새 소식이 나를 끌어당기는 느낌임
    몇 년 전부터 Rust 기반 tmux 세션 관리자 rmuxinator(tmuxinator 클론 느낌)를 직접 개발 중임. 대부분 잘 동작하지만, 삶이 바빠서 진행이 더뎠었고 요즘 버그 수정 위주로 다시 잡고 있음. 최근 추가한 기능은 rmuxinator를 라이브러리로도 쓸 수 있게 만든 것임. tmux-rs를 포크해서 rmuxinator를 의존성으로 넣은 뒤 per-project 설정 파일로 세션을 시작하는 방법이 실제로 먹힐지 테스트해보고 싶음. rmuxinator의 upstream 포함을 주장하는 건 아니지만, 이런 세션 템플릿 기능이 terminal multiplexer 자체에 내장되어 있었다면 정말 유용했을 거라는 생각임
    역으로 rmuxinator가 tmux-rs를 라이브러리로 사용해서 세션 관리 전부를 쉘 커맨드 생성 없이 해결하면 더 좋지 않을까란 생각도 해봄(물론 tmux-rs가 그걸 지원하는지는 아직 모름)
    지금 진행 중인 버그 픽스 마치면, 위 아이디어 중 하나라도 꼭 시도해볼 예정임
    어쨌든 richardscollin 멋진 작업 인정함

  • "tmux를 왜 Rust로 다시 썼냐는 질문에 딱히 좋은 이유는 없고, 그냥 취미 프로젝트다. 정원 가꾸기 같지만 segfault가 더 많다." 이런 태도가 너무 좋음
    새로운 것을 만들 때 꼭 대의명분이나 실용성만 있어야 하는 법은 없음. 취미 프로젝트에서 의외의 발견이 이루어질 수도 있는 것임. 저자의 상세한 글에 감탄
    참고로 내 정원에는 segfault가 넘침. 새로운 프로젝트 코딩이 내 마당엔 더 안전한 느낌임

    • 모두 공감함. 모든 프로젝트가 세상을 바꾸기 위해 존재할 필요는 없음
      최근 fzf를 Rust로 다시 구현한 적이 있음 rs-fzf-clone
      특별한 이유가 있었던 건 아니고, 기존 fzf도 아주 잘 동작했고, 주요 목적은 러스트의 채널과 퍼지 검색 알고리즘을 직접 경험하고 싶었기 때문임. 정말 재미있는 학습 과정이었고, 원래 fzf가 더 뛰어나지만 그게 꼭 중요한 건 아니었음. 새로운 것을 시도하고 실험하는 것 자체가 목적이었음

    • "정원 가꾸기는 철학자가 되기에 가장 좋은 핑계"

      • 레이 브래드버리, 민들레 와인
    • 누군가 Rust가 C보다 무조건 더 우월하다는 뉘앙스를 보이면 반사적으로 냉소적인 반응이 먼저 나오는 성향임. 하지만 사람들이 재미 삼아 이런 프로젝트를 한다는 사실을 자꾸 잊곤 함

    • "우리는 반드시 새로운 것을 만들기 위해서만 이유가 필요한 것은 아니다"란 말이 인상적임
      그런데 tmux는 사실 새로운 건 아님
      기존 소프트웨어를 다른 언어로 다시 쓰는 데에도 이유가 있어야 하는지 생각해보게 됨

    • "정원 가꾸기 같지만 segfault가 더 많다"란 말이 재밌음. Rust에는 아직 익숙하지 않은데, 구체적으로 어떤 상황에서 unsafe가 필요한지 궁금함

  • 이 프로젝트의 태도와, 대부분의 댓글이 긍정적인 분위기라 매우 인상적임
    성숙한 애플리케이션을 다른 언어로 다시 쓴다면 항상 좋지 않다는 평이 있지만, 실제로 시도하면서 많은 배움이 따라오는 것임. 결과보다 과정이 진짜 중요함
    여기 받은 관심과 AI 발전 추세를 감안하면, Rust 입문자에게 아주 매력적인 취미 프로젝트로 발전할 수 있을 것 같음. 쉬운 버그부터 고치고, 새로운 기능 추가하거나 최적화하는 게 큰 경험임
    하나 아이디어로, Gemini CLI(혹은 좋아하는 LLM)를 scratch buffer처럼 만들어 tmux 세션의 다양한 창/패널들과 상호작용하게 하는 기능 제안하고 싶음
    내 경우, 여러 서버에 동기화된 패널로 명령을 실행하다 수동으로 실패 등을 관리하는데, AI에게 명령 실행을 맡기고, 실시간으로 출력을 분석해 적응적으로 커맨드를 재생성하는 기능이 있으면 마치 동적으로 생성되는 맞춤형 셸 스크립트 느낌일 것 같음

    • 취미 프로젝트로 무엇을, 어떤 방식으로 하든 다 존중함. 그런데 왜 기존 소프트웨어를 그냥 한 언어에서 다른 언어로 그대로 포팅하는 데서 흥미를 느끼는지 이해가 안 감
      예를 들어 매일 gvim을 쓰지만, 에디터를 만들고 싶으면 꼭 gvim처럼 만들 필요 없이 내가 원하는 기능만 가진 새로운 것을 창의적으로 만들고 싶다는 쪽임. 이만큼 시간투자 한다면 창의적으로 독특한 걸 시도하는 것이 더 의미 있다고 생각함
  • 방금 tmux를 Fil-C로 1시간 이내에 포팅해봄(libevent 포팅과 테스트 통과 포함). 아주 잘 동작하고 완전한 메모리 안전성 확보 경험임

  • 이런 프로젝트가 마음에 듦. 나도 rust에 빠져보고 싶은 생각 있음
    참고로 zellij(러스트 기반 터미널 멀티플렉서)를 소개하고 싶음
    나는 사용자일 뿐이며, rust 기반 솔루션을 계속 찾아보고, 옮겨가는 걸 즐김

  • 마침 이 영상 "Oxidise Your Command Line"을 방금 보고 있었음
    https://www.youtube.com/watch?v=rWMQ-g2QDsI
    일부는 Rust 개발자가 아니면 필요 없을 수 있지만, 커맨드라인 환경에 익숙한 누구에게나 꽤 유용한 팁도 있음

  • c2rust에서 상수 이름 유지 등, 저자가 지적한 정보 손실을 줄여줄 수 있는 개선이 충분히 가능하지 않을까 생각함. 첫 변환의 부담이 크니까

    • 정말 C2Rust에는 이 기능이 꼭 필요하다고 봄. 내가 이해하기로 이 툴의 핵심 목적은 나중에 idiomatic Rust로 포팅할 베이스 코드를 생성해주는 것임. 그렇지만 상수 정의 같은 게 다 날아가 버리면 생산성 손실이 심각함
  • 대형 언어모델로 C 코드 전체를 Safe Rust로 한 시간만에, 그리고 정확하게 자동 변환해주는 시대가 오면 이런 프로젝트가 아주 미래지향적 대표 사례 될 것 같음
    다만, 저자도 마지막 단계에서 Cursor로 시도해봤지만(2025년 중순 기준), 변환 효율이 현저히 떨어졌다고 해서 현실적 성능은 아직 멀었다고 봄

    • codemod.com 등에서 이걸 이미 "codemods"라는 개념으로 시도하는 중임
      codemods는 AST(추상 구문 트리)를 이용해 빠르고 대량의 코드 변환 및 리팩터링을 가능하게 함
      codemods API refactoring 소개

    • "대형 언어 모델로 복잡한 C코드를 1시간만에 Safe Rust로 완벽하게 변환할 수 있다"는 부분이 구체적이라서 인상 깊게 느껴짐

  • 앞으로 코드가 더 깔끔해지길 기대함. zellij를 여러 번 시도해봤는데, 여러 해의 개발에도 tmux에서 편하게 제공하는 기능 몇 가지가 아직 부족함
    특히 status bar 숨김/표시 불가가 가장 불편함
    zellij-org/zellij 이슈 #694 참고

    • 세션 매니저 플러그인에 단축키를 매핑할 수 없어서 내게는 쓸 수 없음
      내가 빈번히 사용하는 키 바인딩이 세션 매니저 플러그인 기본 바인딩이랑 겹쳐서, 디렉토리 선택 등 주요 기능이 막혀버림
      결국 세션 생성도 플러그인 대신 커맨드라인에서 직접 해야 하는 구조임