우리는 러스트를 하면서 매크로를 다양하고 많이 접하게됩니다.
써본적 없다고요? 그러면 println!
은요?
이처럼 매크로는 다양한 형태로 여러분 곁에 존재합니다.
그리고 러스트의 매크로는 아직 진화 중이기에 이 글이 나중에는 맞지않을 수도 있습니다.
하지만 그럼에도 다루는 이유는 러스트의 핵심적이기도 하고 자주 사용되며, 중추적인 역할을 해내기 때문입니다.
그럼 우리는 이제 다음과 같은 내용들을 알아 볼 것 입니다.
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
}
};
}
실제 코드에서는 정확한 양의 메모리를 할당하는 코드가 추가되어있습닏.
#[macro_export]
이는 매크로가 정의된 crate
가 scope
에 들어갈때만 사용할 수 있어야함을 알립니다.
만약 이게 없다면, scope
로 가져올 수 없습니다.
macro_rules!
이는 매크로를 선언함을 알리며, 이름을 정의합니다.
( $( $x:expr ), * ) => { { } }
본문의 경우 match
표현식과 굉장히 유사합니다.
패턴이 일치한다면 해당 화살표를 따라가 실행하는거죠.
vec!
의 경우 갈래가 하나뿐이지만, 더 복잡한 매크로들은 여러개겠죠?
그리고 여기서 사용되는 값 비교 패턴은 다음 등에서 사용되는 패턴과 다릅니다
리터럴 값(Literals)
분해한 배열(Array), 열거형(Enum), 구조체(Struct), 튜플(Tuple)
변수(Variable)
와일드카드(Wildcard)
임시 값(Placeholders)
위 친구들에서 사용하는 패턴은 추후 다루겠습니다.
그리고 매크로에서 사용되는 패턴은 레퍼런스를 참고해주시길 바랍니닷
물론 여기서 사용되는 것들은 여기서 다룰 예정입니다.
$( $x:expr )
$
이후 괄호쌍이 오며 안에 인자값들이 지정됩니다.
$x:expr
은 Rust 표현식
과 매칭되며, $x
라는 이름을 지정해줍니다.
또한 $x
는 일반적인 변수가 아닌, 매크로 변수임을 특징해줍니다.
$( $x:expr ), *
이후 나오는 *
은 앞에 있는 선행 표현식($( $x:expr )
)이
0개 이상 매칭되게 합니다.
고로, 다음과 같은 경우에 1, 2, 3
모두 매칭되는거죠
let v: Vec<u32> = vec![1, 2, 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
와 비교 패턴
으로 찾아오겠습니다.