Rust, macro

오병진·2022년 8월 25일
0

Learnning Rust

목록 보기
7/7

우리는 러스트를 하면서 매크로를 다양하고 많이 접하게됩니다.

써본적 없다고요? 그러면 println!은요?

이처럼 매크로는 다양한 형태로 여러분 곁에 존재합니다.
그리고 러스트의 매크로는 아직 진화 중이기에 이 글이 나중에는 맞지않을 수도 있습니다.

하지만 그럼에도 다루는 이유는 러스트의 핵심적이기도 하고 자주 사용되며, 중추적인 역할을 해내기 때문입니다.

그럼 우리는 이제 다음과 같은 내용들을 알아 볼 것 입니다.

목차

  1. 매크로가 무엇인가, 그리고 함수랑 뭐가 다른것인가
  2. macro_rules!를 통한 선언적 매크로

매크로가 무엇인가, 함수랑 뭐가 다른가

작동방식

일반적인 함수는 런타임 중에 호출되고 작동합니다.

반면 매크로는 메타프로그래밍이기에 컴파일 도중에
Rust 코드를 작성하고,
확장합니다.

코드를 작성하는 코드이기에 관리가 더 복잡하기도하고요 :D

사용방식

또한 함수는 정해진 인자값만큼 받아올 수 있지만,
매크로는 이 부분에 대해서 굉장히 가변적입니다.
println!만 봐도 알 수 있을만큼요.

println!("hello");

println!("hello {}", name);

호출방식

그리고 가장 중요한 차이점으로

어디에서나 정의하고 어디에서나 호출하는 함수와 달리
매크로를 사용하기전에는 정의하고 scope내로 가져와야합니다.

macro_use를 통한 선언적 매크로

Rust에서 가장 널리 사용되는 매크로 선언방식입니다.

macros by example,
macro_rules! macros,
macros.

라고도 불리기도 합니다.

뭐 이건 중요하지않으니까요

vec!를 통해 macro_use!가 어떻게 사용되는지 알아 볼 예정입니다.

let v: Vec<u32> = vec![1, 2, 3];

위 코드는 세 정수를 받아 벡터로 생성하는 코드입니다.

이외에도 vec!는 두 정수로 이루어진 벡터 또는 5개의 스트링으로 이어진 벡터를 만드는데도 사용할 수 있습니다.

하지만 값의 유형이나 수를 미리 알지 못하기에 함수로 구현 불가능합니다.

그리고 아래의 코드는 간략화 된 vec! 코드입니다.

#[macro_export]
macro_rules! vec {
    ( $( $x:expr ),* ) => {
        {
            let mut temp_vec = Vec::new();
            $(
                temp_vec.push($x);
            )*
            temp_vec
        }
    };
}

실제 코드에서는 정확한 양의 메모리를 할당하는 코드가 추가되어있습닏.

  1. #[macro_export]
    이는 매크로가 정의된 cratescope에 들어갈때만 사용할 수 있어야함을 알립니다.
    만약 이게 없다면, scope로 가져올 수 없습니다.

  2. macro_rules!
    이는 매크로를 선언함을 알리며, 이름을 정의합니다.

  3. ( $( $x:expr ), * ) => { { } }
    본문의 경우 match 표현식과 굉장히 유사합니다.

    패턴이 일치한다면 해당 화살표를 따라가 실행하는거죠.

    vec!의 경우 갈래가 하나뿐이지만, 더 복잡한 매크로들은 여러개겠죠?

    그리고 여기서 사용되는 값 비교 패턴은 다음 등에서 사용되는 패턴과 다릅니다

  • 리터럴 값(Literals)

  • 분해한 배열(Array), 열거형(Enum), 구조체(Struct), 튜플(Tuple)

  • 변수(Variable)

  • 와일드카드(Wildcard)

  • 임시 값(Placeholders)

    위 친구들에서 사용하는 패턴은 추후 다루겠습니다.
    그리고 매크로에서 사용되는 패턴은 레퍼런스를 참고해주시길 바랍니닷

    물론 여기서 사용되는 것들은 여기서 다룰 예정입니다.

  1. $( $x:expr )
    $ 이후 괄호쌍이 오며 안에 인자값들이 지정됩니다.

    $x:exprRust 표현식과 매칭되며, $x라는 이름을 지정해줍니다.
    또한 $x는 일반적인 변수가 아닌, 매크로 변수임을 특징해줍니다.

  2. $( $x:expr ), *
    이후 나오는 *은 앞에 있는 선행 표현식($( $x:expr ))이
    0개 이상 매칭되게 합니다.

    고로, 다음과 같은 경우에 1, 2, 3 모두 매칭되는거죠

    let v: Vec<u32> = vec![1, 2, 3];
  3. $( temp_vec.push($x); )*
    이는 temp_vec.push($x)*에 매칭된 갯수만큼 반복시킨다는 것이며,
    temp_vec.push($x)$x에는 각자 매칭됐던 값들이 들어갑니다.

    그러므로 다음과 같은 코드가 나타나는거죠

    {
      let mut temp_vec = Vec::new();
      temp_vec.push(1);
      temp_vec.push(2);
      temp_vec.push(3);
      temp_vec
    }

마무리하면서

가장 기초적인 매크로의 형태와, 함수와의 차이점을 알아봤습니다.
다음은 이번 매크로에서 언급된 match비교 패턴으로 찾아오겠습니다.

profile
지나가는 사람입니다. 마저 지나갈게요 :D

0개의 댓글