rustc는 `nightly-2025-11-21`부터 기존 기본값이었던 C++ 맹글링 재사용 방식 대신 자체 "v0" 맹글링 방식을 nightly에서 기본으로 사용합니다.
요약: rustc는 nightly-2025-11-21부터 기존 기본값이었던 C++의 맹글링 방식을 재사용하던 방식 대신 자체 "v0" 맹글링 방식을 nightly 버전에서 기본으로 사용합니다.
Rust가 오브젝트 파일과 바이너리로 컴파일될 때, 각 항목(함수, 정적 항목 등)은 이를 식별하는 전역적으로 고유한 "심볼"을 가져야 합니다.
C에서는 함수의 심볼 이름이 단순히 그 함수가 정의된 이름 자체이며, 예를 들어 strcmp와 같습니다. 이는 직관적이고 이해하기 쉽지만, 각 항목이 링크되는 라이브러리의 어떤 심볼과도 겹치지 않는 전역적으로 고유한 이름을 가져야 함을 의미합니다. 두 항목이 같은 심볼을 가진다면 링커가 심볼을 메모리 주소(예를 들어 함수의 주소)로 해석하려 할 때 어떤 심볼이 올바른 것인지 알 수 없게 됩니다.
Rust와 C++ 같은 언어는 타입 시스템의 정보를 활용해 각 항목에 고유한 심볼 이름을 부여하는 "심볼 맹글링 방식"을 정의합니다. 이것이 없다면 여러 방식으로 심볼 충돌이 발생할 수 있습니다. 예를 들어 겉으로 보이는 언어 수준에서는 이름이 모두 같은 제네릭 또는 템플릿 함수의 각 인스턴스화(C++의 오버로드도 포함)는 결국 서로 충돌하는 심볼을 만들게 됩니다. 또는 a::foo와 b::foo처럼 서로 다른 모듈에 같은 이름이 있어도 심볼이 충돌하게 됩니다.
Rust는 원래 C++에서 (때때로) 사용하는 Itanium ABI의 이름 맹글링 방식을 바탕으로 한 심볼 맹글링 방식을 사용했습니다. 그 후 여러 해 동안, 원래 그 맹글링 방식이 설계되지 않았던 Rust 기능들을 지원하기 위해 일관성 없고 임시방편적인 방식으로 확장되었습니다. 현재 Rust의 레거시 맹글링 방식에는 여러 단점이 있습니다.
. 문자가 들어갈 수 있습니다디버거나 프로파일러에서 Rust를 사용하려다가 어떤 함수가 무엇인지 알아내기 어려웠던 적이 있다면, 아마도 맹글링 방식에서 정보가 사라지고 있었기 때문일 것입니다.
Rust 컴파일러 팀은 2018년에 RFC 2603으로 자체 맹글링 방식 작업을 시작했습니다(현재 형식 문서는 rustc 책의 "v0 Symbol Format" 장을 참고하세요). 우리의 "v0" 맹글링 방식은 여러 장점을 가집니다.
A-Z, a-z, 0-9, _만 사용하도록 제한되어 다양한 플랫폼의 도구와의 호환성을 높입니다하지만 rustc만이 Rust 심볼 이름과 상호작용하는 유일한 도구는 아닙니다. 앞서 언급한 디버거, 프로파일러 및 기타 도구들도 Rust 사용자가 맹글링된 심볼을 직접 보지 않고도 익숙한 도구들로 Rust 바이너리를 계속 다룰 수 있도록 Rust의 v0 심볼 맹글링 방식을 이해하도록 업데이트되어야 합니다. 또한 이 모든 도구는 새 릴리스를 배포해야 하고, 그 릴리스들이 배포판에 반영되어야 합니다. 이 과정에는 시간이 걸립니다.
다행히도 컴파일러 팀은 이제 우리의 v0 맹글링 방식 지원이 충분히 널리 퍼졌다고 보고 있으며, rustc가 이를 기본값으로 사용하기 시작할 수 있다고 판단했습니다.
Rust 백트레이스를 읽거나, 컴파일된 Rust 코드에 동작하는 디버거, 프로파일러 및 기타 도구에서 훨씬 더 유용하고 읽기 쉬운 이름을 출력할 수 있게 됩니다. 이는 특히 async 코드, 클로저, 제네릭 함수에서 큰 도움이 됩니다.
새 맹글링 방식이 실제로 어떻게 보이는지는 다음 예제로 쉽게 확인할 수 있습니다.
fn foo<T>() {
panic!()
}
fn main() {
foo::<Vec<(String, &[u8; 123])>>();
}
레거시 맹글링 방식에서는 foo의 제네릭 인스턴스화에 관한 유용한 정보가 모두 f::foo 심볼에서 사라집니다.
thread 'main' panicked at f.rs:2:5:
explicit panic
stack backtrace:
0: std::panicking::begin_panic
at /rustc/d6c...582/library/std/src/panicking.rs:769:5
1: f::foo
2: f::main
3: core::ops::function::FnOnce::call_once
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
하지만 v0 맹글링 방식에서는 f::foo::<alloc::vec::Vec<(alloc::string::String, &[u8; 123])>>와 같이 제네릭 인스턴스화의 유용한 세부 정보가 보존됩니다.
thread 'main' panicked at f.rs:2:5:
explicit panic
stack backtrace:
0: std::panicking::begin_panic
at /rustc/d6c...582/library/std/src/panicking.rs:769:5
1: f::foo::<alloc::vec::Vec<(alloc::string::String, &[u8; 123])>>
2: f::main
3: <fn() as core::ops::function::FnOnce<()>>::call_once
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
v0 맹글링 방식을 사용하는 심볼은 레거시 맹글링 방식의 심볼보다 더 커질 수 있으며, 이로 인해 심볼이 제거되지 않은 경우(기본적으로 제거되지 않습니다) 링크 시간과 바이너리 크기가 약간 증가할 수 있습니다. 다행히 이 영향은 작을 것으로 보이며, 특히 Rust가 이제 일부 타깃에서 기본으로 사용하게 될 lld 같은 현대적인 링커에서는 더욱 그렇습니다.
컴파일러 팀이 알지 못하는 오래된 버전의 도구나 배포판, 또는 특수한 도구들에는 v0 맹글링 방식 지원이 추가되지 않았을 수 있습니다. 이런 도구를 사용할 때의 유일한 결과는 사용자가 맹글링된 심볼을 마주칠 수 있다는 점입니다. 도구가 Rust 심볼 디맹글링을 지원하지 않는다면 rustfilt를 사용할 수 있습니다.
어떤 경우든 문제가 발생하면 새 맹글링 방식 사용을 비활성화할 수 있습니다. -Csymbol-mangling-version=legacy -Zunstable-options 플래그를 사용하면 레거시 맹글링 방식으로 되돌릴 수 있습니다.
레거시 맹글링 방식을 명시적으로 활성화하려면 nightly가 필요합니다. 이는 장기적으로 지원을 제거할 수 있도록 안정화할 의도가 없습니다.
Rust 심볼과 상호작용하는 도구를 유지 관리하고 있으며 아직 v0 맹글링 방식을 지원하지 않는다면, 프로젝트에 통합할 수 있는 v0 심볼 디맹글러의 Rust 및 C 구현이 rust-lang/rustc-demangle 저장소에 제공됩니다.
rustc는 내일의 rustup nightly(nightly-2025-11-21)부터 모든 타깃에서 우리의 "v0" 맹글링 방식을 nightly 기본값으로 사용합니다.
문제가 발생하면 GitHub에서 이슈를 열어 알려주세요.
그런 일이 생기면 -Csymbol-mangling-version=legacy -Zunstable-options 플래그로 레거시 맹글링 방식을 사용할 수 있습니다. 일반적인 RUSTFLAGS 환경 변수에 추가하거나, 프로젝트의 .cargo/config.toml 설정 파일에 다음과 같이 추가하면 됩니다.
[build]
rustflags = ["-Csymbol-mangling-version=legacy", "-Zunstable-options"]
새 심볼 맹글링 버전이 마음에 들고 Rust의 stable 또는 beta 채널에서도 사용해보고 싶다면, 오늘부터 RUSTFLAGS 또는 .cargo/config.toml을 통해 -Csymbol-mangling-version=v0 플래그를 비슷한 방식으로 사용할 수 있습니다.
[build]
rustflags = ["-Csymbol-mangling-version=v0"]