接下來的系列博客文章將包含我對Rust 標準庫的研究。我已經在分散的地方編寫了本系列的部分內容,希望將它們集中在一個地方,以便更好、更容易地訪問。每當我發現一些有趣/重要的事情需要記住時,我就會更新。

我指的是Rust stable v1.33.0中的實現。

這篇文章涵蓋了我認為重要的std::vec的一些細節。

Vec<T>

Vec<T> 是一個動態數組,它只會自動增長而不會自動收縮。

具有堆分配內容的連續可增長數組類型。

-- Rust std doc

注意其對應的堆棧分配的固定大小數組[T; N]之間的差異 (此時N需要是一個指定的非負整數。常量泛型有望很快出現)。

好的!讓我們開始吧。Vec<T> 包含(指針容量長度)。

Vec 指針

  1. 指針永遠不會為空(never be null),因此它享受空指針優化(null-pointer-optimization)。
  2. 指針可能不會實際指向已分配的內存,例如Vec::new(), vec![]Vec::with_capacity(0)

Vec 容量和長度

  1. 向量的容量是指分配給未來元素的空間量,這些元素將被添加到向量上。
  2. 長度是在向量中推入/插入的實際元素的數量。
  3. Vec 分配內存 mem::size_of::<T>() * capacity() > 0. 因此,即使具有正容量,也不會為零大小型(ZST)分配。
  4. 當長度與容量匹配時, 將按一定的增長因子進行(重新)分配。 這使得插入 amortized O(1) 。現在 的增長因子是2。 但是,與其他語言(如C ++,Java等)相比,鑒於任何全局首選分配器,它似乎並不是最佳選擇。 啟發式的, 1.5或者比黃金比例略小的數字被認為是最佳的。這是當前打開的相關 issue,我發現很有趣。
  5. 縮水因子呢?比如, 如果我們取出一半的元素,將釋放四分之一的內存? 不,實際上!如果取出所有元素,容量將不會改變,從而在堆上留下一個漏洞。 因此,如果你要釋放一些內存,請使用shrink_to_fit
  6. 如果需要將 Vec 用於 FFI 或作為 memory-backed 的集合,請確保使用from_raw_parts釋放內存,然後顯式刪除。
  7. 如果在FFI中使用並且需要作為指針傳遞,為了安全起見請記住在傳遞(as_mut_ptr() or as_ptr())之前調用 shrink_to_fittruncate 以避免傳遞未初始化的內存緩衝區。
  8. 如果 coerced into slice,元素的順序總是保證相同。

這是簡化的定義:

struct Vec<T> {
buf: RawVec<T>,
len: usize,
}
// Default Global allocator
struct RawVec<T, A: Alloc = Global> {
ptr: Unique<T>,
cap: usize,
a: A,
}
#[repr(transparent)]
struct Unique<T: ?Sized> {
pointer: *const T,
_marker: PhantomData<T>,
}

Unique<T>

  1. #[repr(transparent)] 強制 Unique<T>類型與 *const T 保持一致。
  2. Unique<T>*mut T (wrt T) 的協變版本,並且具有比 NonNull<T>更強的語義。
  3. *mut T不同, 指針必須總是為 non-null.
  4. 事實上, Box<T> 包裝了 Unique<T>,即 struct Box<T: ?Sized>(Unique<T>)
  5. 它可以在 nightly 版本使用,通過 #![feature(ptr_internals)] and core::ptr::Unique
  6. 如果 TSend/Sync 那麼 Unique<T> 也是 Send/Sync
  7. PhantomData<T>標記的僅對於 rustc dropck 才能理解我們邏輯上擁有一個T導致Unique<T>NonNull<T>之間的主要區別,其中它被定義為

// NonNull<T> doesnt own the referent whereas Unique<T> does
#[repr(transparent)]
struct NonNull<T: ?Sized> {
pointer: *const T,
}

本文翻譯自 Rust std study series: Vec


本文轉載自 Rust 標準庫學習:Vec


推薦閱讀:
相關文章