本文簡要介紹rust語言中 Struct alloc::vec::Vec
的用法。
用法
pub struct Vec<T, A: Allocator = Global> { /* fields omitted */ }
一種連續可增長的數組類型,寫作Vec<T>
,發音為'vector'。
例子
let mut vec = Vec::new();
vec.push(1);
vec.push(2);
assert_eq!(vec.len(), 2);
assert_eq!(vec[0], 1);
assert_eq!(vec.pop(), Some(2));
assert_eq!(vec.len(), 1);
vec[0] = 7;
assert_eq!(vec[0], 7);
vec.extend([1, 2, 3].iter().copied());
for x in &vec {
println!("{}", x);
}
assert_eq!(vec, [7, 1, 2, 3]);
提供 vec!
宏是為了方便初始化:
let mut vec1 = vec![1, 2, 3];
vec1.push(4);
let vec2 = Vec::from([1, 2, 3, 4]);
assert_eq!(vec1, vec2);
它還可以使用給定值初始化Vec<T>
的每個元素。這可能比在單獨的步驟中執行分配和初始化更有效,尤其是在初始化零向量時:
let vec = vec![0; 5];
assert_eq!(vec, [0, 0, 0, 0, 0]);
// The following is equivalent, but potentially slower:
let mut vec = Vec::with_capacity(5);
vec.resize(5, 0);
assert_eq!(vec, [0, 0, 0, 0, 0]);
有關詳細信息,請參閱容量和重新分配。
使用 Vec<T>
作為高效堆棧:
let mut stack = Vec::new();
stack.push(1);
stack.push(2);
stack.push(3);
while let Some(top) = stack.pop() {
// Prints 3, 2, 1
println!("{}", top);
}
索引
Vec
類型允許按索引訪問值,因為它實現了 Index
特征。一個例子會更明確:
let v = vec![0, 2, 4, 6];
println!("{}", v[1]); // it will display '2'
但要小心:如果您嘗試訪問不在 Vec
中的索引,您的軟件將會出現Panics!你不可以做這個:
let v = vec![0, 2, 4, 6];
println!("{}", v[6]); // it will panic!
如果要檢查索引是否在 Vec
中,請使用 get
和 get_mut
。
切片
Vec
可以是可變的。另一方麵,切片是隻讀對象。要獲得 slice ,請使用 &
。例子:
fn read_slice(slice: &[usize]) {
// ...
}
let v = vec![0, 1];
read_slice(&v);
// ... and that's all!
// you can also do it like this:
let u: &[usize] = &v;
// or like this:
let u: &[_] = &v;
在 Rust 中,當您隻想提供讀取訪問權限時,將切片作為參數而不是向量傳遞更為常見。 String
和 &str
也是如此。
容量和重新分配
向量的容量是為將添加到向量上的任何未來元素分配的空間量。不要與向量的長度混淆,向量的長度指定向量中實際元素的數量。如果一個向量的長度超過了它的容量,它的容量會自動增加,但它的元素必須重新分配。
例如,容量為 10 且長度為 0 的向量將是一個空向量,其空間可容納 10 個以上的元素。將 10 個或更少的元素推送到向量上不會改變其容量或導致發生重新分配。但是,如果向量的長度增加到 11,則必須重新分配,這可能會很慢。出於這個原因,建議盡可能使用 Vec::with_capacity
來指定向量的預期大小。
保證
由於其令人難以置信的基本性質,Vec
對其設計做出了很多保證。這確保它在一般情況下盡可能為low-overhead,並且可以通過不安全的代碼以原始方式正確操作。請注意,這些保證是指不合格的 Vec<T>
。如果添加了額外的類型參數(例如,支持自定義分配器),覆蓋它們的默認值可能會改變行為。
最根本的是,Vec
是並且永遠都是(指針、容量、長度)三元組。不多也不少。這些字段的順序是完全未指定的,您應該使用適當的方法來修改它們。指針永遠不會為空,所以這個類型是null-pointer-optimized。
但是,指針可能實際上並不指向分配的內存。特別是,如果您構建一個Vec
容量為 0 通過Vec::new
,alloc::vec,alloc::vec::Vec.with_capacity,或通過調用alloc::vec::Vec.shrink_to_fit在一個空的 Vec 上,它不會分配內存。同樣,如果您將零大小的類型存儲在Vec
,它不會為它們分配空間。請注意,在這種情況下,Vec
可能不會報告 capacity
為 0.Vec
當且僅當mem::size_of::<T>() * capacity() > 0
.一般來說,Vec
的分配細節非常微妙——如果你打算使用Vec
並將其用於其他用途(傳遞給不安全的代碼,或構建您自己的memory-backed 集合),請務必使用from_raw_parts
恢複Vec
然後放下它。
如果一個Vec
已分配的內存,那麽它指向的內存在堆上(由分配器定義,Rust 配置為默認使用),它的指針指向len
按順序初始化的連續元素(如果將其強製為切片,您會看到什麽),然後是capacity - len
邏輯上未初始化的連續元素。
包含元素 'a'
和 'b'
且容量為 4 的向量可以如下所示進行可視化。頂部是Vec
結構,它包含指向堆中分配頭的指針、長度和容量。底部是堆上的分配,一個連續的內存塊。
ptr len capacity
+--------+--------+--------+
| 0x0123 | 2 | 4 |
+--------+--------+--------+
|
v
Heap +--------+--------+--------+--------+
| 'a' | 'b' | uninit | uninit |
+--------+--------+--------+--------+
- 初始化表示未初始化的內存,請參見
MaybeUninit
. - 注意:ABI 不穩定,
Vec
不保證其內存布局(包括字段的順序)。
Vec
永遠不會執行 “small optimization” 其中元素實際存儲在堆棧中的原因有兩個:
-
這將使不安全代碼更難以正確操作
Vec
。如果僅移動Vec
的內容,則它的內容將沒有穩定的地址,並且很難確定Vec
是否實際分配了內存。 -
它會懲罰一般情況,每次訪問都會產生一個額外的分支。
Vec
永遠不會自動收縮自身,即使完全為空。這確保不會發生不必要的分配或釋放。清空 Vec
然後將其填充回相同的 len
應該不會調用分配器。如果您想釋放未使用的內存,請使用 shrink_to_fit
或 shrink_to
。
alloc::vec::Vec.push和alloc::vec::Vec.insert如果報告的容量足夠,則永遠不會(重新)分配。alloc::vec::Vec.push和alloc::vec::Vec.insert 將要(重新)分配如果len == capacity
.也就是說,報告的容量是完全準確的,並且可以依賴。它甚至可以用於手動釋放由Vec
如果需要的話。批量插入方法可能重新分配,即使沒有必要。
Vec
不保證任何特定的增長策略在重新分配時已滿,也不保證任何特定的增長策略alloc::vec::Vec.reserve叫做。當前的策略是基本的,並且可能證明使用非恒定增長因子是可取的。無論使用什麽策略,當然都會保證O(1)攤銷alloc::vec::Vec.push.
vec![x; n]
、 vec![a, b, c, d]
和 Vec::with_capacity(n)
都將生成具有完全請求容量的 Vec
。如果 len == capacity
,(如 vec!
宏的情況),則可以將 Vec<T>
轉換為 Box<[T]>
和從 Box<[T]>
轉換,而無需重新分配或移動元素。
Vec
不會專門覆蓋從中刪除的任何數據,但也不會專門保留它。其未初始化的內存是臨時空間,它可以隨意使用。它通常隻會做最有效或最容易實現的事情。請勿出於安全目的而依賴刪除已刪除的數據。即使您刪除 Vec
,其緩衝區也可能會被另一個分配重用。即使您首先將 Vec
的內存清零,實際上也可能不會發生,因為優化器不認為這是必須保留的副作用。不過,有一種情況我們不會打破:使用unsafe
代碼寫入多餘的容量,然後增加長度以匹配,總是有效的。
目前,Vec
不保證刪除元素的順序。順序過去已更改,可能會再次更改。
相關用法
- Rust VecDeque.binary_search_by_key用法及代碼示例
- Rust VecDeque.rotate_left用法及代碼示例
- Rust VecDeque.remove用法及代碼示例
- Rust VecDeque.swap_remove_back用法及代碼示例
- Rust Vec.drain用法及代碼示例
- Rust Vec.into_raw_parts用法及代碼示例
- Rust VecDeque.contains用法及代碼示例
- Rust VecDeque.iter用法及代碼示例
- Rust VecDeque.split_off用法及代碼示例
- Rust Vec.resize用法及代碼示例
- Rust VecDeque.reserve_exact用法及代碼示例
- Rust VecDeque.capacity用法及代碼示例
- Rust VecDeque.is_empty用法及代碼示例
- Rust Vec.swap_remove用法及代碼示例
- Rust VecDeque.resize用法及代碼示例
- Rust VecDeque.back用法及代碼示例
- Rust VecDeque.new用法及代碼示例
- Rust VecDeque.resize_with用法及代碼示例
- Rust Vec.is_empty用法及代碼示例
- Rust VecDeque.as_mut_slices用法及代碼示例
- Rust VecDeque.front用法及代碼示例
- Rust Vec.reserve_exact用法及代碼示例
- Rust Vec.retain_mut用法及代碼示例
- Rust VecDeque.try_reserve_exact用法及代碼示例
- Rust Vec.try_reserve_exact用法及代碼示例
注:本文由純淨天空篩選整理自rust-lang.org大神的英文原創作品 Struct alloc::vec::Vec。非經特殊聲明,原始代碼版權歸原作者所有,本譯文未經允許或授權,請勿轉載或複製。