1P by GN⁺ | ★ favorite | 댓글 1개
  • Cpp2Rust는 clang AST 기반의 구문 주도 번역기로, C++ 입력을 자동으로 완전히 안전한 Rust 코드로 변환함
  • 번역 과정은 clang으로 C++ 파일을 파싱해 AST를 만들고, AST를 순회하며 Rust 코드를 문자열로 생성한 뒤 rustfmt로 단일 .rs 파일을 출력함
  • 기본값은 참조 카운팅 모델이며, 디버깅과 성능 비교를 위해 --model=unsafe로 unsafe Rust 생성도 가능함
  • 생성된 코드는 libcc2rs 런타임 라이브러리에 의존하며, C 포인터는 null, 산술, 별칭을 모델링하는 Ptr<T>로 변환됨
  • 전체 프로그램 번역에는 compile_commands.json이 필요하며, CMake 프로젝트는 CMAKE_EXPORT_COMPILE_COMMANDS=ON 플래그로 생성할 수 있음

C++에서 안전한 Rust로 자동 변환

  • Cpp2Rust는 C++를 완전히 안전한 Rust로 자동 번역하는 도구임
  • clang의 AST를 기반으로 하는 구문 주도 번역기
  • 번역 알고리듬은 PLDI 2026에 게재된 논문 Cpp2Rust: Automatic Translation of C++ to Safe Rust에 설명돼 있음

번역 파이프라인

  • 입력 C++ 파일을 먼저 clang으로 파싱해 AST를 생성함
  • 이후 AST를 순회하며 Rust 코드를 문자열로 출력함
  • 필요한 경우 libcc2rs 런타임 라이브러리 호출을 삽입함
    • 예시는 raw pointer semantics 처리임
  • 마지막으로 rustfmt를 사용해 Rust 코드를 단일 .rs 파일로 정리함

안전한 Rust와 unsafe Rust 모델

  • 기본 동작은 참조 카운팅 모델을 사용하며, 완전히 안전한 Rust 출력을 생성함
  • unsafe Rust 생성기도 제공됨
    • 명령행 인자는 --model=unsafe
    • 용도는 디버깅과 성능 비교임

libcc2rs 런타임 라이브러리

  • 생성된 코드는 번역 과정을 단순화하기 위해 설계된 런타임 라이브러리에 의존함
  • C 포인터는 libcc2rs가 제공하는 Ptr<T> 타입으로 변환됨
  • Ptr<T>는 C 포인터 의미를 모델링함
    • null
    • 포인터 산술
    • 별칭
  • Rust의 borrow checker는 검사된 런타임 연산을 통해 만족함

빌드와 실행 흐름

  • Ubuntu 의존성 설치 예시는 libclang-22-dev, clang++-22, ninja-build, cmake, ruff를 포함함
  • 빌드는 cmake -GNinja .., ninja, ninja check 순서로 진행함
  • 단일 파일 번역 명령은 다음 형태임
./build/cpp2rust/cpp2rust --file=<file>.cpp -o=<file>.rs
  • unsafe Rust를 생성하려면 다음처럼 실행함
./build/cpp2rust/cpp2rust --file=<file>.cpp -o=<file>.rs --model=unsafe

최소 예제와 전체 프로그램 번역

  • hello.cppprintf("hello world\n") 예제는 Rust의 println!("hello world")를 포함한 코드로 변환됨
  • 변환된 hello.rs는 다음처럼 컴파일하고 실행함
rustc hello.rs -L ../libcc2rs/target/debug
./hello
  • 전체 프로그램 번역에는 compile_commands.json이 필요함
  • CMake에서는 다음 플래그로 compile_commands.json을 생성함
cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ..
  • 전체 프로그램 번역 명령은 다음 형태임
./build/cpp2rust/cpp2rust --dir=<dir> -o <output>.rs
  • <dir>compile_commands.json이 들어 있는 디렉터리여야 함

테스트 명령

  • 전체 테스트는 ninja check로 실행함
  • 단위 테스트는 ninja check-unit로 실행함
  • libcc2rs 단위 테스트는 ninja check-libcc2rs로 실행함
  • libcc2rs-macros 단위 테스트는 ninja check-libcc2rs-macros로 실행함
  • 의도적으로 변경한 뒤 예상 출력을 재생성하려면 REPLACE_EXPECTED=1 ninja check-unit을 사용함

댓글과 토론

Lobste.rs 의견들
  • 올해 2월 Dan Wallach의 발표를 들었는데, DARPA의 TRACTOR(Translating All C to Rust) 프로젝트를 소개했음 [1] [2] [3]
    연구 프로그램의 시험·평가는 MIT Lincoln Laboratory 팀이 수행 중이라고 함
    아직 연구 단계로 알고 있지만 이 주제에 관심이 많고, 이런 도구와 아이디어가 어떻게 나올지 꽤 궁금함
    메모리 관리에서 오는 많은 “사소한” 버그를 실제로 없애줄 수 있을 것 같음
    [1] https://www.darpa.mil/research/programs/translating-all-c-to-rust
    [2] https://github.com/DARPA-TRACTOR-Program
    [3] https://ll.mit.edu/r-d/projects/…

  • 흥미로움. 저자들이 이걸로 변환했고 스스로도 흥미롭다고 보는 라이브러리나 앱 데모를 한두 개 볼 수 있으면 정말 좋겠음

  • 변환 단계가 필요한지 잘 모르겠음. 메모리 안전한 Rust로 변환할 만큼 충분한 정보가 있다면, C++도 정적 분석으로 안전하다고 보장할 수 있을 만큼 정보가 있는 것 아닌가?

    • 변환 과정에서 새 검사를 넣는 방식이라, “원래 코드가 안전했다”기보다는 “새 코드는 메모리를 망가뜨리는 대신 크게 실패하며 종료된다”는 절충에 가까움
    • README에 링크된 논문 첫머리에 이 내용이 있음
      널리 배포된 소프트웨어 보안 취약점의 약 70%는 C와 C++ 같은 언어의 메모리 안전성 버그에서 나오고, 정적 분석·새니타이저·하드웨어 격리 같은 완화책에 수십 년 투자했지만 공격자는 여전히 안전하지 않은 메모리 연산을 악용한다고 함
      장기적으로 유망한 해법은 기존 C++ 코드베이스를 Rust 같은 메모리 안전 언어로 옮기는 것이지만, 수작업으로 하기는 비용이 지나치게 크고 오류가 나기 쉽다고 설명함
      Cpp2Rust는 C++ 프로그램을 기능적으로 동등하고 메모리 안전한 Rust 코드로 자동 변환할 수 있는 첫 시스템이라고 주장함
      성능 일부를 보안과 맞바꾸면서, C++의 제한 없는 별칭 참조와 Rust의 소유권 모델 사이의 근본적 불일치를 런타임 소유권·가변성 검사 삽입으로 해결해 의미를 보존하면서 안전성을 보장한다고 함
      동적 검사 오버헤드를 줄이기 위해 Rust 코드용 소스 간 최적화도 만들었고, 중복 소유권 연산을 제거해 잃은 성능 상당 부분을 회복한다고 함
      그래서 동기는 단순히 안전성을 증명하는 게 아니라, Rust로 이식하고 앞으로의 개발을 Rust로 옮기기 쉽게 하려는 쪽임
    • 충분한 정보가 없고, 라이스 정리에 따르면 앞으로도 없을 것임
      이런 프로젝트의 아이디어는 관용적인 Rust로 더 리팩터링하기 위한 출발점을 만드는 것이고, 그렇게 되면 코드를 분석 가능하게 만들 수 있음
      바라건대 변환된 코드에서 런타임 검사나 안전하지 않은 부분도 제거할 수 있게 됨
      C++를 “Rust 모양”으로 리팩터링해서 똑같이 분석 가능하게 만들면 되지 않느냐고 할 수 있지만, C++는 더 많은 유연성을 허용하는 다른 의미론을 갖고 있어서 분석해야 할 경로가 지수적으로 늘거나 정적 분석이 애매한 막다른 길에 빠질 수 있음
      정적 분석기에 까다로운 일을 하지 않고 경계 사례를 악용하지 않겠다고 약속해야 하며, 그러려면 더 제한된 의미론과 추가 애너테이션을 가진 C++ 방언을 만들 수 있음
      Circle/Safe C++가 가능성을 보였지만 C++ WG가 그 방향을 강하게 거부했으니, 지원받지 못하는 Rust 비슷한 C++ 방언을 직접 쓰거나 그냥 Rust를 쓰는 선택만 남음
  • 입력 코드 자체가 안전하지 않은 경우에는 정확히 어떻게 동작하는 걸까?

    • 런타임 검사로 처리함. 그래서 위반이 발생하는 순간 메모리를 망가뜨리는 대신 안전하고 결정적으로 크래시할 수 있음