Deref와 DerefMut: 스마트 포인터의 마법
Deref 트레이트는 러스트의 스마트 포인터(Smart Pointers)가 일반 참조(reference)처럼 자연스럽게 동작하도록 만드는 핵심 메커니즘입니다. 이 트레이트를 구현하면, 특정 타입의 인스턴스에 역참조 연산자(*)를 사용했을 때의 동작을 직접 정의할 수 있습니다. Box<T>, String, Arc<T>와 같은 표준 라이브러리의 많은 타입들이 이 Deref의 마법 위에서 동작합니다.
Deref 트레이트란?
Deref 트레이트는 deref라는 단 하나의 메서드를 요구합니다.
pub trait Deref {
// 역참조했을 때 얻게 될 목표 타입을 지정합니다.
type Target: ?Sized;
// `&self`를 받아 내부 데이터에 대한 참조 `&Self::Target`을 반환합니다.
fn deref(&self) -> &Self::Target;
}Deref를 구현한 타입의 값 x에 대해 *x를 사용하면, 컴파일러는 이를 *(x.deref())로 변환하여 처리합니다.
직접 Deref 구현하기: MyBox<T>
Box<T>와 유사한 우리만의 스마트 포인터 MyBox<T>를 만들어 Deref를 구현해 보겠습니다.
use std::ops::Deref;
struct MyBox<T>(T);
impl<T> MyBox<T> {
fn new(x: T) -> MyBox<T> {
MyBox(x)
}
}
// MyBox<T>에 대해 Deref를 구현합니다.
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
// 튜플 구조체의 첫 번째 필드(데이터)에 대한 참조를 반환합니다.
&self.0
}
}
fn main() {
let x = 5;
let y = MyBox::new(x);
assert_eq!(5, x);
// `*y`는 컴파일러에 의해 `*(y.deref())`로 처리되어,
// MyBox 내부의 값 5에 접근할 수 있게 됩니다.
assert_eq!(5, *y);
}Deref의 꽃: 역참조 강제 변환 (Deref Coercion)
Deref의 진정한 강력함은 **역참조 강제 변환(Deref Coercion)**이라는 기능에서 나옵니다. Deref를 구현한 타입의 참조(&MyBox<T>)가 함수나 메서드 호출 시 인자로 전달될 때, 컴파일러는 필요에 따라 deref 메서드를 연쇄적으로 호출하여 타입을 자동으로 변환해 줍니다.
이는 다음과 같은 편리함을 제공합니다.
// (MyBox 구현은 위와 동일)
// 이 함수는 `&str` 슬라이스를 인자로 받습니다.
fn display_str(s: &str) {
println!("{}", s);
}
fn main() {
// `MyBox`로 감싼 `String`
let m = MyBox::new(String::from("Rust"));
// `&m`의 타입은 `&MyBox<String>` 입니다.
// 하지만 `display_str` 함수는 `&str` 타입을 요구합니다.
// 그런데도 아래 코드는 완벽하게 동작합니다!
display_str(&m);
}어떻게 이것이 가능할까요? 컴파일러는 다음 순서로 Deref Coercion을 수행합니다.
&m(&MyBox<String>)에서.deref()를 호출하여&String을 얻습니다.String또한Deref<Target = str>을 구현하므로,&String에서.deref()를 한 번 더 호출하여&str을 얻습니다.- 최종적으로 얻어진
&str타입이display_str함수가 요구하는 타입과 일치하므로, 함수 호출이 성공합니다.
이러한 자동 변환 덕분에 우리는 번거로운 &(*m)[..] 같은 코드를 작성할 필요 없이, 스마트 포인터를 마치 내부 데이터인 것처럼 편리하게 사용할 수 있습니다.
DerefMut: 가변 역참조
DerefMut 트레이트는 Deref의 가변(mutable) 버전입니다. deref_mut 메서드를 구현하여 * 연산자를 통해 내부 데이터에 대한 가변 참조를 얻고 값을 수정할 수 있게 해줍니다.
결론
Deref 트레이트는 단순한 문법적 설탕을 넘어, 스마트 포인터와 같은 래퍼(wrapper) 타입을 만들 때 사용성과 표현력을 극대화하는 강력한 도구입니다. 역참조 강제 변환(Deref Coercion)을 통해 서로 다른 타입들을 매끄럽게 연결해 줌으로써, 러스트 코드를 더욱 유연하고 간결하게 만들어주는 일등공신이라 할 수 있습니다.