TL;DR

  • Rust의 #[derive(...)]는 현재 파생 구현에 #[inline]을 붙이는 경우가 많으며, 단순 구현에는 유리하지만 중첩된 오류 유형에서는 바이너리 크기를 늘릴 수 있음.
  • Debug, Display, Clone 같은 핵심 트레이트는 #[derive(...)]로 구현하는 방식이 흔함.
  • #[inline]은 보장된 동작은 아니지만, Rust 레퍼런스의 예시와 매크로 확장 결과에서 파생 구현에 포함되는 것을 확인할 수 있음.
  • 중첩된 오류 계층의 Debug 구현은 호출 때마다 인라인될 수 있는 코드가 많으며, 디버그 또는 추적 로그에서 반복 호출될 수 있음.
  • #[inline(never)]를 적용하는 자체 프로시저 매크로를 사용해 uv 바이너리 크기를 약 160KB 줄였으며, 이는 모든 프로그램에서 인라인 제한이 바람직하다는 뜻은 아님.

핵심 트레이트 파생과 inline

  • Rust에서 Debug, Display, Clone 같은 핵심 트레이트를 구현하는 가장 흔한 방법 중 하나는 #[derive(...)]를 사용하는 것임.
  • 간단한 구조체에 #[derive(Debug)]를 적용하고 매크로를 확장하면, 생성된 Debug::fmt 구현에 #[inline]이 포함됨.
  • #[inline] 포함은 보장된 동작으로 보이지 않지만, 레퍼런스의 예시에서 암시되며 매크로 확장 결과에서도 확인 가능함.
  • #[inline]은 힌트일 뿐이며, 일반적으로 단순한 Debug, Clone 등의 파생 구현은 인라인의 이점을 얻음.

중첩된 오류 유형에서 커지는 코드

  • 예시의 오류 계층은 여러 String 필드를 가진 ErrorA, ErrorA를 포함하는 ErrorB, ErrorB를 포함하는 ErrorC, 그리고 세 유형을 변형으로 갖는 Errors 열거형으로 구성됨.
  • 생성된 Debug 구현은 구조체 필드와 열거형 변형을 각각 처리하며, 각 구현 함수에 #[inline]이 붙음.
  • Errors의 Debug 구현을 호출할 때마다 이 계층의 상당한 코드가 인라인될 수 있으며, 디버그 또는 추적 로그에서 반복 호출되는 경우가 있음.
  • 실제 Rust 애플리케이션의 오류 열거형 계층은 예시보다 훨씬 깊고 필드 수도 적지 않은 경우가 많음. 제시된 계층은 크게 단순화된 예시임.

바이너리 크기 영향

  • 파생된 Debug 구현을 인라인하지 않도록 하자 바이너리 크기를 약 160KB 줄일 수 있었음.
  • 이를 위해 derive(Debug)처럼 동작하지만 #[inline(never)]를 적용하는 자체 프로시저 매크로를 추가함.
  • 코드 크기 비용은 빠르게 누적될 수 있으며, rustc가 Debug 구현의 크기나 인라인 횟수에 제한을 두지 않는 듯하다는 점도 놀라움의 원인임.
  • 다만 많은 프로그램에서는 현재의 인라인 결정이 적절할 것으로 보임.