본문 바로가기
Rust

[Rust] `println!` 매크로가 참조(borrow)로 동작하는 이유

by Nhahan 2024. 9. 1.

 

사전지식:

매크로와 함수의 차이

  • 함수는 특정 타입의 인수를 받아 런타임에 실행되고, 파라미터의 타입이 강제된다.
  • 매크로는 함수의 일부로 컴파일되어 동작(사실 당연한 말..!)하고, 파라미터의 타입이 자유롭다.

 


 

`println!`의 내부 동작

#[macro_export]
macro_rules! println {
    () => {
        $crate::print!("\n")
    };
    ($($arg:tt)*) => {{
        $crate::io::_print($crate::format_args_nl!($($arg)*));
    }};
}

$crate: 매크로가 정의된 크레이트 내의 경로를 지정하여, 해당 크레이트에 정의된 함수나 매크로를 사용할 수 있도록 함.

$($arg:tt)*: 반복 패턴으로, 다양한 입력 인자를 토큰 트리(tt)로 받아 처리.

 

여기서 `format_args_nl!`을 봐야한다. 다른 코드는 아직 딱히 borrow와 관련이 없다.

 

 

`format_args_nl!`의 내부 동작

macro_rules! format_args_nl {
    ($($arg:tt)*) => {
        format_args!($($arg)*, "\n")
    };
}

여기 또한 `format_args!`을 봐야한다. 다른 코드는 아직까지도 딱히 borrow와 관련이 없다.

 

 

`format_args!`의 내부 동작

macro_rules! format_args {
    ($fmt:expr, $($arg:tt)*) => {{
        $crate::fmt::Arguments::new_v1(
            &[$fmt],
            &[$($crate::format_args_argument!($arg))*]
        )
    }};
}

드디어 나왔다. `&[$fmt]`와 `&[$($crate::format_args_argument!($arg))*]`의 `&`가 borrow로 동작하는 근거.

 

`fmt::Arguments`까지도 내부 동작을 본다면,

pub struct Arguments<'a> {
    pieces: &'a [&'a str],
    pub(crate) args: &'a [ArgumentV1<'a>],
}

여기에서 또한 `&`를 사용하고 있음으로 borrow로 동작한다는 사실을 알 수 있다.

 


 

p.s.

그런데 왜 `println!`에 파라미터를 넣을때 `&변수명` 형태로 명시적으로 borrow를 라고 나타내지 않아도 될까?

매크로는 파라미터의 타입을 강제하지 않고, 다양한 형태의 코드를 받아들일 수 있다. 이 덕분에 `println!`은 어떤 타입이든 유연하게 처리할 수 있고, 컴파일러가 자동으로 참조를 생성해줘서 명시적으로 `&변수명`을 붙이지 않아도 되는 것이다.
(영어로는 이를 type-agnostic라고 하는 듯)

 

 

'Rust' 카테고리의 다른 글

[Rust] Rust의 let/mut과 Map, JS의 const와 Map  (0) 2024.09.18
[Rust] 재밌는 enum  (0) 2024.08.26

댓글