Rust 標準庫學習:Vec
接下來的系列博客文章將包含我對Rust 標準庫的研究。我已經在分散的地方編寫了本系列的部分內容,希望將它們集中在一個地方,以便更好、更容易地訪問。每當我發現一些有趣/重要的事情需要記住時,我就會更新。
我指的是Rust stable v1.33.0中的實現。
這篇文章涵蓋了我認為重要的std::vec的一些細節。
Vec<T>
Vec<T>
是一個動態數組,它只會自動增長而不會自動收縮。
具有堆分配內容的連續可增長數組類型。
-- Rust std doc
注意其對應的堆棧分配的固定大小數組[T; N]
之間的差異 (此時N需要是一個指定的非負整數。常量泛型有望很快出現)。
好的!讓我們開始吧。Vec<T>
包含(指針、容量、長度)。
Vec 指針
- 指針永遠不會為空(never be null),因此它享受空指針優化(null-pointer-optimization)。
- 指針可能不會實際指向已分配的內存,例如
Vec::new(), vec![]
或Vec::with_capacity(0)
。
Vec 容量和長度
- 向量的容量是指分配給未來元素的空間量,這些元素將被添加到向量上。
- 長度是在向量中推入/插入的實際元素的數量。
Vec
分配內存mem::size_of::<T>() * capacity() > 0
. 因此,即使具有正容量,也不會為零大小型(ZST)分配。- 當長度與容量匹配時, 將按一定的增長因子進行(重新)分配。 這使得插入 amortized O(1) 。現在 的增長因子是2。 但是,與其他語言(如C ++,Java等)相比,鑒於任何全局首選分配器,它似乎並不是最佳選擇。 啟發式的, 1.5或者比黃金比例略小的數字被認為是最佳的。這是當前打開的相關 issue,我發現很有趣。
- 縮水因子呢?比如, 如果我們取出一半的元素,將釋放四分之一的內存? 不,實際上!如果取出所有元素,容量將不會改變,從而在堆上留下一個漏洞。 因此,如果你要釋放一些內存,請使用
shrink_to_fit
。 - 如果需要將
Vec
用於 FFI 或作為 memory-backed 的集合,請確保使用from_raw_parts
釋放內存,然後顯式刪除。 - 如果在FFI中使用並且需要作為指針傳遞,為了安全起見請記住在傳遞(
as_mut_ptr()
oras_ptr()
)之前調用shrink_to_fit
或truncate
以避免傳遞未初始化的內存緩衝區。 - 如果 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>
#[repr(transparent)]
強制Unique<T>
類型與*const T
保持一致。Unique<T>
是*mut T
(wrtT
) 的協變版本,並且具有比NonNull<T>
更強的語義。- 與
*mut T
不同, 指針必須總是為 non-null. - 事實上,
Box<T>
包裝了Unique<T>
,即struct Box<T: ?Sized>(Unique<T>)
。 - 它可以在 nightly 版本使用,通過
#![feature(ptr_internals)]
andcore::ptr::Unique
。 - 如果
T
是Send/Sync
那麼Unique<T>
也是Send/Sync
。 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
推薦閱讀: