Vecの実装 in Rust - メモリ解放

前回 に引き続き、 The RustnomiconImplementing Vec をやってみる。

コード全体は GitHub上のリポジトリ にある。

rustcのバージョンは以下のとおり。

$ rustc --version
rustc 1.25.0-nightly (27a046e93 2018-02-18)

Deallocating

確保したメモリは使わなくなったら解放しなくてはいけない。 Drop for Vec を実装し、その中で解放処理を書くことにする。 ここでも新しい メモリアロケータAPI を使う。

self.cap == 0 のときはメモリ確保していないので、解放もしなくてよい。 self.cap == 1 のときは、pop() することで要素をdropし、 Alloc::dealloc_one<T>() を使う。 それ以外の場合は、すべての要素を順に pop() することでdropし、 Alloc::dealloc_array<T>() を呼ぶ。

impl<T> Drop for Vec<T> {
    fn drop(&mut self) {
        if self.cap == 0 {
            return;
        }

        while let Some(_) = self.pop() {}

        unsafe {
            if self.cap == 1 {
                self.alloc.dealloc_one(self.ptr.as_non_null());
            } else {
                let e = self.alloc.dealloc_array(self.ptr.as_non_null(), self.cap);
                if let Err(e) = e {
                    self.alloc.oom(e);
                }
            }
        }
    }
}

なお、T: !Drop の場合は pop() を呼ぶ処理を省略できる。 T: Drop かどうかは mem::needs_drop() で判定できる。

if mem::needs_drop::<T>() {
    while let Some(_) = self.pop() {}
}

しかし、この最適化を施しても効果はほぼ見られなかった。 LLVMの最適化がかなり強いらしい。

ちなみに、 標準ライブラリの Drop for Vec の実装 では、 ptr::drop_in_place() を使って Drop for [T] にフォールバックしているようだ。