レイアウト
まず、構造体のレイアウトを考える必要があります。 Vec は 3 つの部品を 持っています。アロケーションへのポインタと、アロケーションの大きさ、 そして初期化された要素の数です。
愚直に考えると、これは以下の設計で良いということになります。
pub struct Vec<T> { ptr: *mut T, cap: usize, len: usize, } fn main() {}
そして実際に、このコードはコンパイルできます。残念ながら、この設計は正しくありません。
まず、コンパイラはあまりに厳密すぎる変性を与えることになります。ですから
&Vec<&'a str>
が予期されているところで &Vec<&'static str>
を使う事が
出来ません。もっと重要なことに、この設計によって正しくない所有権の情報が
ドロップチェッカに渡されてしまいます。型 T
のいかなる値も所有していないと、
ドロップチェッカが保守的に判断してしまうからです。変性やドロップチェックに
関する全ての詳細は、所有権とライフタイムの章を参照してください。
所有権の章で見てきたように、所有しているアロケーションに対する生ポインタを持つ場合、
*mut T
の代わりに Unique<T>
を使用するべきです。 Unique はアンステーブルなため、
可能なら使いませんが。
繰り返しになりますが、 Unique は生ポインタのラッパで、以下のことを宣言 します。
T
に対して変性- 型
T
の値を所有する可能性がある (ドロップチェックのため) T
が Send/Sync を実装している場合、継承される*mut T
に参照外しをする (つまりコード内では専ら*mut
のように振る舞う)- ポインタはヌルにはならない (つまり
Option<Vec<T>>
はヌルポインタ最適化される)
上記の最後以外の項は、安定版の Rust で実装可能です。
use std::marker::PhantomData; use std::ops::Deref; use std::mem; struct Unique<T> { ptr: *const T, // 変性のために *const です _marker: PhantomData<T>, // ドロップチェッカ対策 } // Send と Sync を継承することは安全です。なぜならこのデータの // Unique を所有しているからです。 Unique<T> は "単なる" T のようなものです。 unsafe impl<T: Send> Send for Unique<T> {} unsafe impl<T: Sync> Sync for Unique<T> {} impl<T> Unique<T> { pub fn new(ptr: *mut T) -> Self { Unique { ptr: ptr, _marker: PhantomData } } } impl<T> Deref for Unique<T> { type Target = *mut T; fn deref(&self) -> &*mut T { // 参照も受け取っている時に、 *const を *mut に // キャストする方法はありません。 // これらは全て "ただのポインタ" ですのでトランスミュートします。 unsafe { mem::transmute(&self.ptr) } } } fn main() {}
残念ながら、値が非 0 であると述べるメカニズムはアンステーブルで、すぐには 安定版はならないでしょう。ですから単に std の Unique を使うことにします。
#![feature(unique)] use std::ptr::{Unique, self}; pub struct Vec<T> { ptr: Unique<T>, cap: usize, len: usize, } fn main() {}
もしヌルポインタ最適化を気にしないなら、安定版のコードを使用することもできます。
しかしながら、残りのコードでは、最適化を有効にするような設計していきます。
特に、 Unique::new
を呼ぶことはアンセーフです。なぜなら null
を中に突っ込む
ことは、未定義動作を引き起こしてしまうからです。安定版のコードの new
はアンセーフに
する必要はありません。中身についての興味深い保証をしないからです。