tmux-rs 소개

7 hours ago 1

  • 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 == NULL → ptr.is_null() / ptr->field → (*ptr).field 등 손수 매핑
  • 자동화 도구(Cursor)도 시도했으나, 손실되거나 잘못된 코드가 많아 코드 리뷰 부담이 커짐
  • 손가락 피로를 줄이는 데 일부 도움됐으나, 생산성 측면에선 제한적임

결론 및 향후 방향

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

Read Entire Article