レイアウト

まず、構造体のレイアウトを考える必要があります。 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 はアンセーフに する必要はありません。中身についての興味深い保証をしないからです。

関連キーワード:  Unique, レイアウト, mut, ptr, Vec, ドロップ, コード, ポインタ, Send, 参照