사전지식:
매크로와 함수의 차이
- 함수는 특정 타입의 인수를 받아 런타임에 실행되고, 파라미터의 타입이 강제된다.
- 매크로는 함수의 일부로 컴파일되어 동작(사실 당연한 말)하고, 파라미터의 타입이 자유롭다.
`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 |
댓글