C.W.K.
Stream
Lesson 02 of 04 · published

선언적 매크로 (macro_rules!)

~11 min · macros, declarative, macro-rules

Level 0Rust 호기심러
0 XP0/80 lessons0/19 achievements
0/100 XP to next level100 XP to go0% complete

매크로를 쓰는 일상적 방법은 macro_rules! — 문법에 패턴 매칭으로 작동하는 선언적 매크로야, match 가 값에 작동하듯이.

토큰에 패턴 매칭

macro_rules! 는 규칙을 정의해: 각각 토큰 패턴이랑 입력이 맞을 때 생성할 코드야. 패턴은 메타변수 를 써 — $x:expr (표현식 매칭), $n:ident (식별자), $t:ty (타입). 매크로를 호출하면 컴파일러가 네 입력을 규칙에 매칭하고 맞는 걸 확장해.

반복

진짜 힘은 반복이야: $( $x:expr ),* 가 콤마로 구분된 표현식 리스트를 매칭하고, 출력에서 $( ... )* 로 거기 위에 확장해. 그게 매크로가 인자를 몇 개든 받는 법이야 — 바로 vec! 가 지어진 법. 패턴 하나가 0, 1, 또는 100 원소를 다뤄.

$name:fragment 을 '이런 종류 문법을 캡처해' 로 읽어. $x:expr 은 출력에 붙여넣을 표현식을 캡처; $( ... ),* 은 콤마 구분 리스트를 캡처하고 반복. macro_rules! 가 그냥 반복 있는 문법-매칭이라는 걸 보면, 외계 문법이 읽혀.

언제 쓰나

선언적 매크로는 값이 아니라 문법으로 달라지는 보일러플레이트를 줄이는 도구야 — 커스텀 문법으로 컬렉션 짓기, 반복적 trait impl 생성, 작은 DSL. 토큰 하나만 다른 코드를 복붙하고 있으면 매크로가 약일 수 있어 — 함수나 제네릭을 배제한 뒤에.

Code

macro_rules!: 패턴 + 반복이 vec! 복제를 짓는다·rust
// 미니 vec 매크로: 콤마 구분 표현식을 몇 개든 받음
macro_rules! my_vec {
    ( $( $x:expr ),* ) => {{
        let mut v = Vec::new();
        $( v.push($x); )*   // 캡처된 각 $x 마다 push 확장
        v
    }};
}

fn main() {
    let v: Vec<i32> = my_vec![10, 20, 30];
    println!("{v:?}"); // [10, 20, 30]
    let empty: Vec<i32> = my_vec![];
    println!("{empty:?}"); // []
}

External links

Exercise

표현식 둘 이상을 받아 최댓값으로 확장하는 max! 매크로를 반복을 써서 써봐. max!(3, 7, 2) 로 테스트해. 그다음 물어봐: 이게 대신 제네릭 함수일 수 있었을까? 매크로가 언제 더 나은 선택이야?
Hint
제네릭 fn max(a: T, b: T) 가 두 값을 깔끔하게 다뤄; 매크로는 함수가 표현 못 하는 가변 max!(a, b, c, d) 문법을 원할 때만 값을 해. 두 인자면 충분하면 함수가 더 단순해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.