Rust的Vector
了解 Rust 的 Vec<T>
Rust 是一门强调安全性、性能和并发的现代编程语言。在 Rust 中,Vec<T> 是一个动态大小的数组类型,广泛应用于需要动态存储的数据结构中。Vec 是 Rust 标准库中非常重要的一部分,它提供了灵活和高效的内存管理方式。
什么是 Vec<T>?
Vec<T> 是 Rust 的一个集合类型,属于 "动态数组"。它可以动态增长或缩小,存储类型为 T 的元素。这意味着 Vec<T> 在运行时可以根据需求调整大小,而不像传统的数组那样大小固定。
在 Rust 中,Vec<T> 是一个堆分配的数据结构,与栈上的固定大小数组不同。它允许你在运行时增加或删除元素。
Vec 与传统数组的对比
Rust 中的数组是固定大小的,这意味着在定义时必须指定大小并且不能更改。相比之下,Vec 提供了灵活的存储能力,你可以根据需要动态地增加或删除元素,适用于一些无法预知元素数量的场景。
fn main() {
// 数组
let arr = [1, 2, 3]; // 固定大小
// Vec
let mut vec = Vec::new(); // 创建一个空的 Vec
vec.push(1); // 向 Vec 中添加元素
vec.push(2);
vec.push(3);
// 输出 Vec
println!("{:?}", vec); // 打印: [1, 2, 3]
}
在上面的示例中,arr 是一个固定大小的数组,而 vec 是一个 Vec,你可以随时调用 push() 方法向其中添加元素。
Vec 的常用操作
1. 创建和初始化 Vec
Vec::new()
创建一个空的 Vec,它的初始大小为 0。
let mut v: Vec<i32> = Vec::new();
vec![] 宏
通过 vec![] 宏快速创建并初始化一个 Vec。
let v = vec![1, 2, 3, 4];
Vec::with_capacity(n)
创建一个具有指定容量的空 Vec,可以避免在添加元素时频繁的重新分配内存。
let mut v = Vec::with_capacity(10);
2. 访问和修改元素
get(index)
返回 Vec 中指定索引处的元素,返回 Option 类型,避免越界错误。如果索引越界,返回 None。
let v = vec![1, 2, 3];
let item = v.get(1); // Some(2)
[](索引)
使用索引来访问 Vec 中的元素,若索引越界,会发生 panic。
let v = vec![1, 2, 3];
let item = v[1]; // 2
push(value)
向 Vec 的末尾添加一个元素。
let mut v = vec![1, 2];
v.push(3); // v 变成 [1, 2, 3]
insert(index, value)
在指定索引处插入一个元素。其后的元素将右移。
let mut v = vec![1, 2, 4];
v.insert(2, 3); // v 变成 [1, 2, 3, 4]
remove(index)
删除指定索引的元素,并返回它。删除操作会使得该元素后的元素左移。
let mut v = vec![1, 2, 3, 4];
let removed = v.remove(1); // removed = 2, v 变成 [1, 3, 4]
pop()
从 Vec 的末尾移除并返回一个元素,若 Vec 为空,返回 None。
let mut v = vec![1, 2, 3];
let last = v.pop(); // last = Some(3), v 变成 [1, 2]
resize(new_len, value)
调整 Vec 的大小,如果 new_len 大于当前长度,会用 value 填充新增加的元素;如果 new_len 小于当前长度,会丢弃多余的元素。
let mut v = vec![1, 2, 3];
v.resize(5, 0); // v 变成 [1, 2, 3, 0, 0]
3. 其他常用方法
len()
返回 Vec 中的元素个数。
let v = vec![1, 2, 3];
let length = v.len(); // length = 3
is_empty()
检查 Vec 是否为空。
let v = vec![];
let is_empty = v.is_empty(); // is_empty = true
clear()
清空 Vec 中的所有元素。
let mut v = vec![1, 2, 3];
v.clear(); // v 变成 []
contains(&value)
检查 Vec 是否包含指定的元素。
let v = vec![1, 2, 3];
let has_two = v.contains(&2); // has_two = true
capacity()
返回 Vec 当前分配的容量(即它在堆上可以存储多少个元素而不需要重新分配内存)。
let v = vec![1, 2, 3];
let cap = v.capacity(); // cap = 3
reserve(additional)
提前为 Vec 分配额外的内存,避免后续的重新分配操作。additional 是希望预分配的额外空间。
let mut v = Vec::new();
v.reserve(10); // 预分配至少可以容纳 10 个元素
shrink_to_fit()
将 Vec 的容量缩减到当前的长度,以释放多余的内存。
let mut v = Vec::with_capacity(100);
v.push(1);
v.shrink_to_fit(); // 将容量缩小到 1
split_at(mid)
将 Vec 从 mid 索引分割为两个部分,返回一个元组,包含前半部分和后半部分。
let v = vec![1, 2, 3, 4, 5];
let (first, second) = v.split_at(3);
// first = [1, 2, 3], second = [4, 5]
clone()
克隆 Vec,即深拷贝一个新的 Vec。
let v = vec![1, 2, 3];
let v_clone = v.clone(); // v_clone = [1, 2, 3]
4. 遍历和映射
iter()
返回一个不可变的迭代器,允许你遍历 Vec 中的元素。
let v = vec![1, 2, 3];
for x in v.iter() {
println!("{}", x);
}
into_iter()
返回一个可以消耗 Vec 的迭代器,移走元素。使用后,Vec 会被移除,无法再访问。
let v = vec![1, 2, 3];
for x in v.into_iter() {
println!("{}", x);
}
// v 在这里已经被消耗掉
iter_mut()
返回一个可变的迭代器,允许你修改 Vec 中的元素。
let mut v = vec![1, 2, 3];
for x in v.iter_mut() {
*x *= 2;
}
println!("{:?}", v); // 输出: [2, 4, 6]
map()
通过对 Vec 中的每个元素应用函数,生成一个新的集合。
let v = vec![1, 2, 3];
let v2: Vec<_> = v.iter().map(|x| x * 2).collect(); // v2 = [2, 4, 6]
5. 其他操作
extend(iterable)
将另一个集合的元素添加到 Vec 的末尾。
let mut v = vec![1, 2];
let v2 = vec![3, 4];
v.extend(v2); // v 变成 [1, 2, 3, 4]
retain(pred)
保留满足条件的元素,删除不满足条件的元素。
let mut v = vec![1, 2, 3, 4, 5];
v.retain(|&x| x % 2 == 0); // v 变成 [2, 4]
sort()
对 Vec 中的元素进行排序。
let mut v = vec![3, 1, 4, 1, 5];
v.sort(); // v 变成 [1, 1, 3, 4, 5]
Vec<T> 的内存管理
Rust 的 Vec<T> 是一个智能指针,它在堆上分配内存。Rust 会自动管理 Vec 的内存,当 Vec 被销毁时,它所占用的内存会自动被释放,避免了内存泄漏。
Vec 内部有一块连续的内存,用来存储所有元素。当 Vec 增长时,如果没有足够的空间来容纳新元素,Rust 会自动重新分配更多的内存。这种增长通常是指数级的,因此可以有效减少内存重新分配的次数。
内存拷贝与优化
由于 Vec 在内部使用堆分配内存,当元素增多时,可能会导致内存重新分配和拷贝操作。如果你事先知道 Vec 的大小,可以通过 Vec::with_capacity() 方法来预分配内存,避免在增长过程中频繁的内存重新分配。
fn main() {
// 创建一个初始容量为 10 的 Vec
let mut vec = Vec::with_capacity(10);
vec.push(1);
vec.push(2);
// 检查当前容量
println!("当前容量: {}", vec.capacity());
}
总结
优势:
- 动态大小:无需事先知道元素个数。
- 高效内存管理:Rust 自动管理内存,减少了内存泄漏的风险。
- 灵活性:支持许多常用操作,如添加、删除、访问、遍历等。
缺点:
- 性能损失:由于
Vec在堆上分配内存,相比栈上数组可能会略慢,尤其是在频繁增长或收缩时。

浙公网安备 33010602011771号