본 글은 Phantom를 공부하며 작성한 글입니다. 오역 및 오류가 있을 수 있습니다. 이에 대해서는 댓글로 남겨주시면 감사하겠습니다.
https://stackoverflow.com/questions/41533508/what-is-the-phantomdata-actually-doing-in-the-implementation-of-vec/42720413
Rust standard library의 Vec<T>
implementation을 공부하던 중, PhantomData<T>
데이터 타입을 접하게 되어 이와 관련된 글을 작성하게 되었습니다. PhantomData<T>
는 역할을 알기 위해서는 먼저, Rust에서 Vec<T>
가 어떻게 구현되어 있는지 알아볼 필요가 있습니다.
Vec<T>
의 구조Vec<T>
는 내부적으로 대략 아래와 같은 구조를 가지고 있습니다. (실제로는 ptr과 _marker를 필드로 보유한 Unique<T>
를 소유한 RawVec<T>
을 가지고 있습니다. )
pub struct Vec<T> {
ptr: *const T,
cap: usize,
len: usize,
_marker: PhantomData<T>
}
각 필드를 살펴보면, ptr은 포인터, cap은 capacity, len은 길이를 나타냅니다. 이들은 직관적으로 배열을 구성하는 요소로 생각됩니다. 그렇다면, PhantomData<T>
는 여기서 무슨 역할을 하는 것일까요?
PhantomDat<T>
를 이해하기 위해서는 러스트의 Drop Trait이 어떻게 동작 하는지를 알 필요가 있습니다.
RFC1283 에 따르면, 일반적으로 포인터를 소유하는 것은 데이터를 빌려온(Borrowing) 것입니다. 따라서, 포인터를 소유한데이터 타입의 destructor가 호출될 때 해당 포인터가 가르키는 데이터 타입의 destructor는 호출되지 않습니다. 대부분의 경우, 이는 매우 합리적인 소유권 관리 방법으로 보입니다.
그러나, Vec<T>
는 독특한 데이터 타입입니다.
Vec<T>
는 내부적으로 포인터만을 가지고 있지만, 실질적으로 해당 데이터를 소유하고 있다고 봐도 무방합니다. 따라서, Vec<T>
의 destructor가 호출될 때에는 T의 desturctor도 함께 호출되어야 합니다. PhantomData<T>
는 이러한 정보를 컴파일러에게 전달하는 Marker Trait
입니다.
Unqiue<T>
Rust의 standard library에서는 PhandomData<T>
와 포인터(*const T
)를 합친 Unique<T>
를 제공합니다. Unique<T>
데이터 타입은 Vec<T>
와 같은 자료구조 구현에 활용되고 있습니다. Unique<T>
의 구조는 다음과 같습니다.
pub struct Unique<T: ?Sized> {
ptr: *const T,
_marker: PhantomData<T>,
}
impl<T: Sized> Unique<T> {
/// Creates a new `Unique` that is dangling, but well-aligned
/// This is useful for initializing types which lazily allocate, like `Vec`
fn empty() -> Self {
let ptr = std::mem::align_of::<T>() as *mut T;
Self {
ptr,
_marker: PhantomData,
}
}
}