Open1
ほんのちょっと踏み込んでいく Rust
PhantomData
-
PhantomData<T>
は、Zero Sized Type (ZST) と呼ばれる型で、サイズが 0 で仮想的に与えられた型T
のフィールドが存在しているかのように、コンパイラに伝えることができるものである。
struct PdStruct<T> {
data: i32,
pd: PhantomData<T>,
}
上記の例では、pd
フィールドの型として PhantomData<T>
が指定されており、PdStruct<T>
のサイズを増やさないが、コンパイラにあたかも T
型を所有しているかのように振る舞うように伝えることができる。
-
PhantomData<T>
は、一般的に生ポインタ、利用されていないライフタイムパラメータ、使用されていない型パラメータとよもに使用される。
生ポインタ
use std::marker::PhantomData;
struct MyRawPtrStruct<T> {
ptr: *mut T,
_marker: PhantomData<T>,
}
impl<T> MyRawPtrStruct<T> {
fn new(t: T) -> MyRawPtrStruct<T> {
let t = Box::new(t);
MyRawPtrStruct {
ptr: Box::into_raw(t),
_marker: PhantomData,
}
}
}
-
MyRawPtrStruct
は、ヒープメモリに割り当てられたT
を所有するシンプルなスマートポインタである。 - Rust のコンパイラは、生ポインタのライフタイムや所有権を自動的に推論することはできない。
- この例における
PhantomData<T>
は、T
が実際にフィールドとして必要ないが、MyRawPtrStruct
が実際には生ポインタの先にT
があり、実際にはT
を所有していることを、コンパイラに伝えている。 - これにより、Rust のコンパイラが、drop する順番や所有権周りの処理を正しく行えるようになる。
使用されていないライフタイムパラメータ
use std::marker::PhantomData;
struct Window<'a, T: 'a> {
start: *const T,
end: *const T,
phantom: PhantomData<&'a T>,
}
-
start
とend
フィールドは生ポインタであり、T
型の値の開始アドレスと終了アドレスをポイントしているが、ライフタイムの情報が含まれておらず、Rust の借用チェッカーが'a
のライフタイムを使うことができない。 -
phantom
フィールドによって、ライフタイム'a
の情報を Rust の借用チャッカーに伝えることができ、Window
がポイントする先のデータは、Window
が使用されている間は drop することができないと判断できるようになる。
使用されていないタイプパラメータ
TBD.
PhantomData<T>
の実用例
BorrowedFd
pub struct BorrowedFd<'fd> {
fd: RawFd,
_phantom: PhantomData<&'fd OwnedFd>,
}
-
OwnedFd
の借用バージョンがBorrowedFd
である。 - これにより
BorrowedFd
が使われている間は、OwnedFd
を drop することはできないことをコンパイラに伝えることができる。
Iter<T>
pub struct Iter<'a, T: 'a> {
ptr: NonNull<T>,
end: *const T,
_marker: PhantomData<&'a T>,
}
-
Iter
がライフタイム'a
に紐づいていることを伝えることができる。 - 参照している
T
よりも長くIter
は生き残ることはできない。