Vec
的基础概念
Vec<T>
是一个动态大小的集合,可以存储任意类型 T
的元素。与固定大小的数组不同,Vec
可以根据需要增长或缩小。它是一个非常强大且灵活的集合类型,广泛用于 Rust 中的数据存储。
存储方式:Vec
会动态管理内存(栈上存储指向堆上数据的指针),元素在堆上分配。
所有权:Vec
拥有其元素的所有权,这意味着当 Vec
被销毁时,所有的元素也会被销毁。
创建 Vec
-
使用关联函数创建
Vec
。// 创建一个空的 Vec let v: Vec<i32> = Vec::new(); // 容器的初始大小 let v = Vec::with_capacity(10); // from 函数创建 let v = Vec::from([1, 2, 3]);
-
使用宏创建
Vec
。// 创建一个空的 Vec let v = vec![]; // 创建一个包含初始元素的 Vec let v = vec![1, 2, 3];
内存管理与性能
在 Rust 中,Vec
使用堆分配内存,并具有智能的内存管理策略。其容量(capacity
)是指其当前为元素预分配的内存大小。Vec
会根据需要增加或减少容量。
容量与长度
-
len()
: 获取Vec
中当前元素的个数。 -
capacity()
: 获取Vec
当前容量的大小,即预分配的内存空间。 -
reserve()
: 向Vec
请求额外的空间,这样可以避免在添加元素时频繁重新分配内存。 -
shrink_to_fit()
: 将Vec
的容量收缩为当前实际使用的内存大小。
重新分配内存
当 Vec
超过当前容量时,它会自动扩大内存,通常会将容量增加到原容量的两倍。这种扩容策略有助于减少重复的内存分配操作。
let mut v = Vec::new();
v.push(1); // 会进行一次内存分配
v.push(2); // 继续在当前分配的内存中插入元素
v.reserve(100); // 请求预分配更多内存(避免后续的重复分配)
println!("Capacity: {}", v.capacity()); // 输出容量
Vec
的增长策略
默认情况下,Vec
会每次扩展容量时,按 2x 的增长因子进行扩展。这样能够有效减少扩展次数,提高性能。
常用操作与方法
添加元素
-
push(T)
:向Vec
的末尾添加一个元素。let mut v = Vec::new(); v.push(1); v.push(2);
-
extend<I>(iterable)
:将一个迭代器中的所有元素追加到Vec
末尾。let mut v = vec![1, 2]; v.extend(vec![3, 4, 5].into_iter()); println!("{:?}", v); // 输出: [1, 2, 3, 4, 5]
删除元素
-
pop()
:删除并返回Vec
末尾的元素。let mut v = vec![1, 2, 3]; let last = v.pop(); println!("{:?}", last); // Some(3)
-
remove(index)
:按索引删除元素。let mut v = vec![1, 2, 3]; let removed = v.remove(1); // 删除索引为1的元素 println!("Removed: {}", removed); // 2 println!("{:?}", v); // [1, 3]
-
clear()
:删除所有元素,Vec
会被清空,但不改变它的容量。let mut v = vec![1, 2, 3]; v.clear(); println!("{:?}", v); // []
索引访问
-
使用索引访问元素。注意,Rust 会在访问时进行边界检查。
let v = vec![10, 20, 30]; println!("{}", v[1]); // 20
-
使用
get()
方法,安全访问元素,返回Option<T>
,可以避免索引越界错误。let v = vec![10, 20, 30]; if let Some(value) = v.get(2) { println!("{}", value); // 30 } else { println!("Out of bounds"); }
遍历元素
-
使用
for
循环遍历:let v = vec![1, 2, 3]; for val in &v { println!("{}", val); // 1 2 3 }
-
使用
iter()
方法返回一个迭代器:let v = vec![1, 2, 3]; v.iter().for_each(|x| println!("{}", x)); // 1 2 3
其他操作
-
合并
Vec
:let mut v1 = vec![1, 2]; let mut v2 = vec![3, 4]; v1.append(&mut v2); // 将 v2 的内容移动到 v1 中 println!("{:?}", v1); // [1, 2, 3, 4] println!("{:?}", v2); // []
-
切片操作:
let v = vec![10, 20, 30, 40]; let slice = &v[1..3]; // 取索引1到2的元素 [20, 30] println!("{:?}", slice); // [20, 30]
切片与数组
-
与切片的转换:
Vec
可以轻松转换为切片,并且切片的生命周期通常短于Vec
的生命周期。let v = vec![1, 2, 3]; let slice: &[i32] = &v[0..2]; // 从 Vec 转为切片
-
从切片创建
Vec
: 切片可以通过.to_vec()
方法转换为Vec
。let slice: &[i32] = &[1, 2, 3]; let v = slice.to_vec(); println!("{:?}", v); // [1, 2, 3]
迭代与操作
Rust 提供了高效的迭代器模式,可以让你对 Vec
进行各种高效操作:
映射与过滤
-
使用
iter().map()
来映射元素。let v = vec![1, 2, 3]; let squared: Vec<i32> = v.iter().map(|x| x * x).collect(); println!("{:?}", squared); // [1, 4, 9]
-
使用
iter().filter()
来过滤元素。let v = vec![1, 2, 3, 4, 5]; let even: Vec<i32> = v.iter().filter(|&&x| x % 2 == 0).cloned().collect(); println!("{:?}", even); // [2, 4]
折叠操作
- 使用
fold()
将Vec
中的元素折叠成一个值。let v = vec![1, 2, 3, 4]; let sum = v.iter().fold(0, |acc, &x| acc + x); println!("{}", sum); // 10
所有权管理
Vec
会遵循所有权规则,当Vec
被移动时,它的所有权会转移,原来的变量将无法继续使用。let v = vec![1, 2, 3]; let v2 = v; // v 的所有权转移给 v2 println!("{:?}", v); // 错误:use of moved value
- 通过引用和借用,你可以避免移动元素,从而提高性能和内存效率。
let v = vec![1, 2, 3]; let v_ref = &v; // 只借用,v 不会移动 println!("{:?}", v_ref); // [1, 2, 3]
应用场景
- 动态存储数据(如读取文件行、网络数据包)。
- 数组替代品,用于需要动态调整大小的场景。
- 配合迭代器链式操作,进行数据转换和处理。