Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

19. Rust 매크로: 전처리기에서 메타프로그래밍까지 🔴

학습 목표:

  • Rust 매크로의 작동 원리를 이해하고, 함수나 제네릭만으로 해결하기 힘든 복잡한 문제를 매크로로 우아하게 해결하는 법을 배웁니다.
  • C/C++ 전처리기와의 차이점, macro_rules! 작성법, 그리고 #[derive] 속성이 내부적으로 어떻게 코드를 생성하는지 마스터합니다.
  • 강력하지만 양날의 검인 매크로를 실무에서 언제, 어떻게 써야 하는지 기준을 세웁니다.

1. 왜 매크로가 필요한가?

Rust의 함수와 제네릭은 강력하지만, 가끔은 코드 자체를 생성해야 하거나 언어의 기본 문법을 확장해야 할 때가 있습니다.

  • 가변 인자(Variadic Arguments): Rust 함수는 인자 개수가 고정되어 있지만, println! 같은 매크로는 원하는 만큼 인자를 받을 수 있습니다.
  • 보일러플레이트 코드 제거: 수십 개의 타입에 대해 거의 동일한 impl 블록을 작성해야 할 때, 매크로 하나로 자동화할 수 있습니다.
  • DSL(도메인 특화 언어): SQL 쿼리나 HTML 템플릿처럼 Rust 문법이 아닌 커스텀 구문을 파싱하여 코드로 바꿀 수 있습니다.

2. 선언적 매크로 (macro_rules!)

가장 많이 쓰이는 매크로 형태로, 패턴 매칭을 통해 코드를 확장합니다. C의 #define과 달리 **구문 트리(AST)**를 인식하며 **위생성(Hygiene)**을 갖추고 있어, 매크로 내부의 변수가 외부 변수와 이름이 겹쳐도 서로 간섭하지 않습니다.

macro_rules! say_hello {
    // 인자가 없는 패턴
    () => { println!("안녕하세요!"); };
    // 인자가 하나 있는 패턴 ($name은 표현식)
    ($name:expr) => { println!("{}님, 반갑습니다!", $name); };
}

fn main() {
    say_hello!();       // 안녕하세요!
    say_hello!("Rust"); // Rust님, 반갑습니다!
}

주요 조각 지정자 (Fragment Specifiers)

  • $x:expr: 표현식 (예: 1 + 2, my_func())
  • $x:ident: 식별자 (변수나 함수 이름 그 자체)
  • $x:ty: 타입 이름 (예: i32, Vec<u8>)
  • $x:tt: 토큰 트리 (가장 유연하며 무엇이든 매칭 가능)

3. 강력한 반복 기능: "리스트를 코드로 바꾸기"

C 매크로는 반복문을 돌릴 수 없지만, Rust 매크로는 $(...)* 문법을 통해 가변 인자 리스트를 순회하며 코드를 생성할 수 있습니다.

#![allow(unused)]
fn main() {
macro_rules! my_vec {
    ( $( $x:expr ),* ) => {
        {
            let mut temp_vec = Vec::new();
            $( temp_vec.push($x); )* // 매칭된 각 요소마다 이 라인을 반복 생성함
            temp_vec
        }
    };
}

let v = my_vec![1, 2, 3]; // [1, 2, 3]을 담은 Vec이 생성됨
}

4. 절차적 매크로 (Procedural Macros)

함수처럼 작동하며 컴파일 타임에 Rust 코드를 입력받아 새로운 Rust 코드를 출력하는 강력한 도구입니다.

  1. Derive 매크로: #[derive(Debug)]처럼 구조체에 기능을 자동으로 붙여줍니다.
  2. 속성(Attribute) 매크로: #[tokio::main]처럼 함수 정의를 통째로 바꿀 수 있습니다.
  3. 함수형 매크로: sql!(SELECT * FROM table)처럼 커스텀 구문을 처리합니다.

💡 실무 권장 사항: "함수가 먼저다"

"함수나 제네릭으로 할 수 있다면 매크로를 쓰지 마세요." 매크로는 강력하지만 에러 메시지가 읽기 어렵고 IDE의 지원을 받기 힘든 경우가 많습니다. 유지보수성을 위해 가급적 평범한 코드를 선호하되, 반복적인 코드 생성이 정말로 필요할 때만 매크로를 도입하는 것이 현명합니다.


📌 요약

  • **macro_rules!**는 패턴 매칭 기반으로 안전하게 코드를 확장합니다.
  • 반복($(...)*) 기능을 활용해 가변 인자나 대량의 보일러플레이트를 처리하세요.
  • 매크로는 텍스트 치환이 아닌 구문 트리(AST) 기반으로 작동함을 이해하세요.
  • 매크로 도입 전 항상 "일반 함수로 해결 가능한가?"를 먼저 고민하세요.