本文简要介绍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。非经特殊声明,原始代码版权归原作者所有,本译文未经允许或授权,请勿转载或复制。