Drop と #[may_dangle]
Drop を手動で実装するジェネリックな Type<T> は、T に #[may_dangle] 属性が適切かどうかを検討すべきです。Nomicon には、#[may_dangle] が何であるかについての詳細があります。
ジェネリックな Type<T> に、T の drop も伴う可能性がある手動の drop 実装がある場合、dropck はそれを知る必要があります。Type<T> による T の所有権が、ManuallyDrop<T>、*mut T、MaybeUninit<T> のようにそれ自体では T を drop しない型を通じて表現されている場合、Type<T> には T が drop される可能性があることを dropck に伝えるための PhantomData<T> フィールドも必要です。標準ライブラリ内で内部の Unique<T> ポインター型を使用している型には、PhantomData<T> マーカーフィールドは必要ありません。それは Unique<T> によって処理されます。
これが実際にどのように問題になり得るかの例として、次のような OptionCell<T> を考えてみましょう。
struct OptionCell<T> {
is_init: bool,
value: MaybeUninit<T>,
}
impl<T> Drop for OptionCell<T> {
fn drop(&mut self) {
if self.is_init {
// Safety: `is_init` が true の場合、`value` は完全に初期化されていることが保証されます。
// Safety: セルは drop されている最中なので、再びアクセスされることはありません。
unsafe { self.value.assume_init_drop() };
}
}
}
PhantomData<T> マーカーフィールドを持たないこの OptionCell<T> に #[may_dangle] 属性を追加すると、OptionCell<T> より厳密には長く生存しない T に対して健全性の穴が生じ、それらが自身の Drop 実装内で、drop された後にアクセスされる可能性がありました。#[may_dangle] を正しく適用するには、PhantomData<T> フィールドも必要でした。
struct OptionCell<T> {
is_init: bool,
value: MaybeUninit<T>,
+ _marker: PhantomData<T>,
}
- impl<T> Drop for OptionCell<T> {
+ unsafe impl<#[may_dangle] T> Drop for OptionCell<T> {
レビュアー向け
手動の Drop 実装がある場合は、#[may_dangle] が適切かどうかを検討してください。適切である場合は、Unique<T> を通じて、または直接フィールドとして、PhantomData<T> も存在することを確認してください。