티스토리 뷰

Rust-Language

매크로

kmj24 2021. 12. 2. 23:33

매크로

매크로는 다른 코드를 작성하는 코드이다.

이 개념은 메타 프로그래밍으로 잘 알려져 있다.

코드의 내용을 출력해주는 print!와 Vector배열을 정의하는 vec!도 매크로이다.

이 모든 매크로들은 수동으로 코드를 작성하지 않고도 많은 코드를 생산할 수 있다.

 

 메타프로그래밍은 함수와 비슷하게 작성해야 할 코드의 양을 줄여주지만, 함수가 할 수 없는 일도 할 수 있다.

함수 시그니처는 해당 함수가 갖는 parameter와 갯수 타입을 선언해야 하는 반면, 매크로는 parameter의 개수를 가변적으로 처리할 수 있다.

// ex
println!("hello");
println!("{}", hello);

 함수와 매크로의 또다른 차이는, 매크로 정의는 함수와 달리 모듈의 "네임스페이스에 소속되지 않는다"는 것이다.

이로 인하여 외부 Crate 사용시 발생하는 예기치 않은 naming충돌 방지를 위하여 외부 Crate를 스코프 내로 가져오는 동시에 #[macro_use] annotation을 사용하여 가져올 매크로를 명시해야 한다.

 

함수 대신 매크로 코드를 사용하는 것에 대한 단점도 있다.

매크로를 구현한다는 것은 러스트 코드를 만들어내는 코드를 작성한다는 것인데, 이는 추상화 계층을 하나 더 만들어 낸다는 의미이기 때문에 함수 정의에 비하여 코드가 복잡해진다.

 

일반적인 메타프로그래밍을 위한 macro_rules!를 사용하는 선언적 매크로

 rust에서는 선언적 매크로의 형태로 가장 널리 사용된다. 

이는 macro by example, macro_rules! 매크로, 아니면 그냥 매크로 라고 불리기도 한다.

선언적 매크로는 rust의 match 표현식과 유사하게 작성할 수 있다.

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

    }
  };
}

vec! 매크로 정의를 간략화한 모습이다.

std library 내 vec!매크로는 메모리의 정확한 양을 미리 할당하는 코드가 포함되어 있다. 

이 코드에서는 간략화하여 표현했다.

 

 #[macro_export] annotation은 정의한 매크로가 들어있는 Crate를 누군가 import 했을 때, 해당 매크로를 사용할 수 있도록 해준다. 즉 export 해준다. 이 annotation을 사용하지 않을 경우 이 크레이트를 dependency로 갖는 누군가,

#[macro_use] annotation을 사용하더라도 해당 매크로는 스코프 내로 가져와지지 않는다. 

 

매크로는 macro_rules! 와 매크로 이름으로 정의할 수 있다.

위의 예시에서 vec!의 본문 구조는 match 표현식 구조와 유사하다. 여기서 ( $ ($x: exper), *) 패턴과 그 뒤로 따라오는 =>, 그리고 해당 패턴에 연관된 코드 블록으로 이루어진 갈래 하나를 찾는다. 패턴이 매칭될 경우, 해당 패턴에 대하여 연관된 코드가 반환된다. 

 

매크로 정의에 사용되는 패턴 문법은 아래 링크에서 확인할 수 있다.

https://doc.rust-lang.org/reference/macros.html#macros

 

Macros - The Rust Reference

The functionality and syntax of Rust can be extended with custom definitions called macros. They are given names, and invoked through a consistent syntax: some_extension!(...). There are two ways to define new macros: Macros by Example define new syntax in

doc.rust-lang.org

 

  1. 괄호 쌍이 전체 패턴을 둘러싼다.
  2. 달러기호($)뒤에 괄호 쌍이 오며, 배치할 코드에서 사용하기 위해, 괄호 안 패턴과 일치하는 값을 캡쳐한다.
  3. $() 내부에 $x: exper가 있는데, 이는 어떤 rust 표현식과도 매치되며, 그에 $x라는 이름을 부여하는 기능을 한다.
  4. $()에 따라오는 쉼표는 $() 내부에 캡처되어 매치된 코드뒤에 나타날 수 있는 리터럴 쉼표 구분 문자를 나타낸다.
  5. 쉼표 뒤의 * 는 자신 앞에 위치한 0개 이상 패턴을 지정한다.
  6. 위에서 작성한 매크로 vec에서 vec![1, 2, 3]; 으로 호출할 경우 $x 패턴은 1, 2, 3 세번의 표현식에 맞추어 세번 매칭된다.
  7. 패턴 갈래와 연관된 본문 코드를 살펴보자면 $() 내부의 temp_vec.push() 코드는 패턴에서 $()에 매치되는 횟수만큼 반복되어 생성되고, 코드 내 $x는 각각 매치된 표현식으로 대체된다.

매크로는 인수 개수가 어느 만큼이건, 인수가 어떤 타입이건 가리지 않고 특정한 요소들을 포함할 벡터를 만들어내는 코드를 생성할 수 있다.

 

매크로 관련해서 다양한 예시이다.

https://danielkeep.github.io/tlborm/book/index.html

 

The Little Book of Rust Macros

The Little Book of Rust Macros Note: this is a work in progress. This book is an attempt to distil the Rust community's collective knowledge of Rust macros. As such, both additions (in the form of pull requests) and requests (in the form of issues) are wel

danielkeep.github.io

 

 

 

 

참고.

https://rinthel.github.io/rust-lang-book-ko/appendix-04-macros.html

 

D - 매크로 - The Rust Programming Language

우린 이 책에서 println! 등의 매크로를 사용했습니다. 하지만 아직 매크로가 정확히 무엇이고, 어떻게 동작하는지는 알아보지 않았습니다. 이번 부록에선 매크로에 대해 다음과 같은 순서로 알아

rinthel.github.io

 

'Rust-Language' 카테고리의 다른 글

trait 다른 타입 간 허용  (0) 2021.09.15
life time, 라이프 타임  (0) 2021.09.12
trait: 공유 동작 정의  (0) 2021.09.08
반복자, iterator  (0) 2021.09.03
Closure, 클로저 함수  (0) 2021.08.05
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
글 보관함