diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 0f6df88..ca083e6 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -18,7 +18,7 @@ * [高階トレイト境界](hrtb.md) * [Subtyping and Variance](subtyping.md) * [Drop Check](dropck.md) - * [PhantomData](phantom-data.md) + * [ファントムデータ](phantom-data.md) * [借用の分割](borrow-splitting.md) * [型変換](conversions.md) * [型強制](coercions.md) diff --git a/src/phantom-data.md b/src/phantom-data.md index 72fa2e2..c947702 100644 --- a/src/phantom-data.md +++ b/src/phantom-data.md @@ -1,9 +1,19 @@ + +# ファントムデータ + + + +アンセーフなコードを扱っているとき、しばしば型やライフタイムが論理的に構造体に結びついているけれども、 +フィールドの一部には結びついていない状況に陥る事があります。 +例えば、 `&'a [T]` に対する `Iter` は (大体) 以下のように定義されます。 ```rust,ignore struct Iter<'a, T: 'a> { @@ -12,20 +22,40 @@ struct Iter<'a, T: 'a> { } ``` + +しかし、 `'a` は構造体の本体内では使用されないため、 `'a` は*無制限*のライフタイムとなります。 +これが過去に引き起こしてきた問題のために、無制限のライフタイムと無制限の型は、 +構造体の定義内では*禁じられています*。それ故に、なんとかして本体内にあるこれらの型を +参照しなければなりません。正しくこれを行なうことは、正しい変性とドロップチェックを +得るために必要です。 + + + +これを、 `PhantomData` という、特別なマーカー型を使って行ないます。 +`PhantomData` はスペースを消費しませんが、静的分析のために、与えられた型のフィールドを装います。 +これは、明白に型システムに、欲しい変種の種類を伝えるよりも、エラーが起こりにくいと思われていた一方、 +例えばドロップチェッカが必要とする情報など、利便なものを提供していました。 + + +Iter は論理的には沢山の `&'a T` を保持しているため、この型が、 PhantomData に +装うよう伝えるものです。 ``` use std::marker; @@ -37,50 +67,94 @@ struct Iter<'a, T: 'a> { } ``` + + +これでよし。ライフタイムには制限が付き、イテレータは `'a` と `T` において変性になります。 +全てうまく行きます。 + + +もう一つ重要な例は Vec で、これは (大体) 以下のように定義されます。 ``` struct Vec { - data: *const T, // *const for variance! + data: *const T, // 変性を得るため *const です! len: usize, cap: usize, } ``` + +前の例と違い、これは、全てが期待しているものと全く同じ*ように見えます*。 +Vec の全てのジェネリックな引数は少なくとも1つのフィールドに現れます。 +さあ準備完了! + + + +いいえ。 + + +ドロップチェッカは、 `Vec` がいかなる型 `T` の値も持たないと惜しみなく決定するでしょう。 +これは結果的に、ドロップチェックの健全性の決定によって、 Vec のデストラクタ内で、 Vec がなにか T の +値をドロップするということを心配する必要がないと結論付けます。 +この結果、 Vec のデストラクタを使用して、無制限という性質を作り出すことを可能にするのです。 + + +ドロップチェックに、 Vec が型 T の値を*本当に*保持していて、それ故に *Vec* が +ドロップする際、なにか T の値もドロップするかもしれないと伝えるために、 +追加の PhantomData を加えなければなりません。 ``` use std::marker; struct Vec { - data: *const T, // *const for covariance! + data: *const T, // 変性を得るため *const です! len: usize, cap: usize, _marker: marker::PhantomData, } ``` + + +アロケーションを持つ生ポインタは普及しているパターンですので、 +標準ライブラリが以下の機能を持つ、 `Unique` と呼ばれるユーティリティを作りました。 + + +* 変性を得るため、 `*const T` をラップします +* `PhantomData` を含みます +* T が Send/Sync を保持しているかのように、自動的に継承します +* ヌルポインタ最適化のため、ポインタを非 0 としてマークします