Rust 프로그램은 `fn main()` 함수에서 시작한다고 알려져 있지만, 실제로는 운영체제(OS) 로더가 바이너리를 메모리에 올리고 제어를 넘기면 C 런타임과 Rust 런타임이 먼저 초기화 작업을 수행합니다. 이 'pre-main' 단계에서는 패닉(panic) 및 언와인딩(unwinding) 처리 준비, C 스타일 프로그램 인자를 Rust의 `std::env::args` 인터페이스로 변환하는 등 핵심적인 런타임 서비스가 구성됩니다. 이처럼 `main` 함수 이전에 실행되는 코드는 프로그램의 안정성과 효율성을 위한 중요한 기반을 다집니다.
개발자는 `#[unsafe(link_section = ".init_array.101")]` 같은 속성이나 `ctor` 크레이트를 활용하여 `main` 함수 이전에 실행될 초기화 함수를 직접 배치할 수 있습니다. 이는 링커(linker)가 빌드 시점에 여러 크레이트(crate)에서 제출된 데이터를 특정 메모리 섹션에 모아주는 원리를 이용합니다. 예를 들어, CLI 서브커맨드 등록이나 문자열 인터닝(interning) 풀 정렬과 같은 작업을 `main` 함수가 시작하기 전에 미리 구성하여, 이후 런타임에서는 잠금(lock) 없이 불변(immutable) 데이터로 효율적으로 접근할 수 있게 합니다. `link-section` 크레이트는 이러한 복잡한 링커 작업을 추상화하여 Rust 슬라이스(slice)처럼 다룰 수 있게 해줍니다.
이러한 `pre-main` 코드 실행 방식은 여러 이점을 제공합니다. 첫째, 모든 데이터가 미리 할당된 연속적인 메모리에 배치되어 런타임에 추가적인 할당이나 크기 조정 없이 효율적인 접근이 가능합니다. 둘째, `main` 이전 단계는 단일 스레드(single-threaded) 환경이므로, 복잡한 동기화 프리미티브(primitive) 없이도 데이터를 안전하게 변경하고 이후 `main` 함수에서는 잠금 없이 읽을 수 있습니다. 이는 의존성 주입(dependency injection)과 유사하게, 제공자와 소비자가 직접 결합하지 않고 링커를 통해 데이터를 수집하는 강력한 패턴을 제공하여 코드의 모듈성과 성능을 향상시킵니다. 하지만 데드 코드 제거의 어려움, 플랫폼별 차이, `Miri` 호환성 한계 등 고려해야 할 제약 사항도 있어 신중한 적용이 필요합니다.