매크로 (marco)
매크로라는 용어는 러스트의 기능군을 의미
marco_rules!를 쓰는 선언적 매크로와 다음 세 가지 종류의 절차적 매크로가 있음
- 커스텀 #[derive] 매크로
구조체와 열거형에 사용되는 derive 속성이 추가된 코드를 지정
- 속성형 (attribute-like) 매크로
모든 아이템에 사용 가능한 커스텀 속성을 정의
- 함수형 (function-like) 매크로
함수 호출처럼 보이지만 지정된 토큰을 인수로써 조작
매크로와 함수의 차이
기본적으로 매크로는 다른 코드를 작성하는 코드를 작성하는 방법
이를 메타프로그래밍(metaprogramming)이라고 함
모든 매크로는 수동으로 작성한 코드보다 더 많은 코드를 생성하기 위해 확장됨
메타프로그래밍은 작성 및 유지 관리해야하는 코드의 양을 줄이는데 유용하며, 이는 함수의 역할 중 하나이기도 함
매크로에는 함수에는 없는 몇 가지 추가 기능이 있음
- 함수 시그니처는 함수에 있는 매개변수의 개수와 타입을 선언해야함 / 반면 매크로는 가변적인 수의 매개변수를 사용할 수 있음
- 또한 매크로는 컴파일러가 코드의 의미를 해석하기 전에 확장되기 때문에 주어진 타입에 대한 트레이트를 구현할 수 있음
함수 대신 매크로를 구현할 때의 단점은 매크로 정의가 러스트 코드를 작성하는 러스트 코드를 작성하는 것이기 때문에 함수 정의보다 복잡함
때문에 매크로 정의는 일반적으로 함수 정의보다 유지보수가 힘듦
그리고 어디서나 정의하고 호출할 수 있는 함수와 달리 매크로의 경우 정의하거나 파일에서 호출하기 전에 매크로를 스코프로 가져와야함
선언적 매크로 (declarative macro)
러스트에서 가장 널리 사용되는 매크로 형태
선언적 매크로는 예제 매크로(macros by example), macro_rules! 매크로, 매크로로 불림
선언적 매크로의 핵심은 러스트 match 표현식과 비슷한 무언가를 작성할 수 있음
let v: Vec<u32> = vec![1, 2, 3];
#[macro_export] // 매크로를 스코프에 가져오기 위함
macro_rules! vec {
( $( $x:expr ), * ) => {
{
let mut temp_vec = Vec::new();
$(
temp_vec.push($x);
)*
temp_vec
}
};
}
위 매크로를 코드로 표현하면 아래와 같음
{
let mut temp_vec = Vec::new();
temp_vec.push(1);
temp_vec.push(2);
temp_vec.push(3);
temp_vec
}
절차적 매크로 (procedural macro)
프로시저의 일종으로 함수처럼 작동하는 매크로
절차적 매크로는 선언적 매크로처럼 패턴에 매칭해보고 코드를 다른 코드로 대체하는 것이 아닌,
어떤 코드를 입력으로 받아서 해당 코드에 대해 작업을 수행한 다음 어떤 코드를 출력으로 생성
절차적 매크로는 커스텀 파생, 속성형, 함수형 세 종류가 있으며 모두 비슷한 방식으로 작동
절차적 매크로 정의 예제
use proc_macro;
#[some_attribute]
pub fn some_name(input: TokenStream) -> TokenStream {
}
커스텀 파생 매크로
- hello_macro_derive/Cargo.toml
[lib]
proc-macro = true
[dependencies]
syn = "1.0"
quote = "1.0"
- hello_macro_derive/src/lib.rs
use proc_macro::TokenStream;
use quote::quote;
use syn;
#[proc_macro_derive(HelloMacro)]
pub fn hello_macro_derive(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();
impl_hello_macro(&ast)
}
fn impl_hello_macro(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
let gen = quote! {
impl HelloMacro for #name {
fn hello_macro() {
println!("my name is {}", stringify!(#name));
}
}
};
gen.into()
}
속성형 매크로
속성형 매크로는 커스텀 파생 매크로와 비슷하지만, derive 속성에 대한 코드를 생성하는 대신 새 속성을 생성할 수 있음
derive는 구조체와 열거형에만 작동하지만 속성은 함수와 같은 다른 아이템에도 적용이 가능
#[route(GET, "/")]
fn index() {}
// 매크로 정의 함수 시그니처
#[proc_macro_attribute]
pub fn route(attr: TokenStream, item: TokenStream) -> TokenStream {}
함수형 매크로
함수형 매크로는 함수 호출처럼 보이는 매크로를 정의
macro_rules! 매크로와 유사하게 함수형 매크로는 함수보다 유연함
이를테면 임의 개수의 인수를 사용할 수 있음
함수형 매크로는 TokenSteam 매개변수를 취하고 그 정의는 러스트 코드를 사용하여 해당 TokenSteam을 조작
#[proc_macro]
pub fn sql(input: TokenStream) -> TokenStream {}
'Programming > Rust' 카테고리의 다른 글
| 러스트의 유용한 도구들 (0) | 2025.10.12 |
|---|---|
| 러스트 고급 함수와 클로저 (0) | 2025.10.12 |
| 러스트 고급 타입 (0) | 2025.10.12 |
| 러스트 고급 트레이트 (0) | 2025.10.12 |
| 안전하지 않은 러스트 (0) | 2025.10.12 |