-
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를 통해 희망함