Vector
约 1287 字大约 4 分钟
2025-08-03
Vec 允许在一个单独的数据结构中储存多于一个的值,它在内存中彼此相邻地排列所有的值。且只能储存相同类型的值,通常会使用 Vec<T> 来表示。
1. Vec 的使用
1.1 创建 Vec
创建一个空的 vector,可以调用 Vec::new 函数,该函数创建 vector 时需要显示标注其类型
let vec: Vec<i32> = Vec::new();更常见的写法是通过宏: vec! 直接使用初始值进行创建,此时 Rust 会自动推断出 vector 的类型
let vec = vec![1, 2, 3];
// 重复填充值
let filled = vec![0; 4]; // [0, 0, 0, 0]注
- 空 Vec 只记录指针、长度和容量,不会立刻分配元素空间
- 使用
Vec::with_capacity(n)可以根据需要预留容量,以减少多次分配
1.2 读取 Vec
读取 vector 中存储的值有两种方式: 使用索引或调用 get 方法
1.2.1 通过索引获取
重要
索引访问默认假定索引一定有效,越界将直接 panic
索引读取 Vec
fn main() {
let v = vec![1, 2, 3, 4, 5];
// 通过索引获取
let third: &i32 = &v[2];
println!("The third element by index is {}", third);
// 越界:会直接 panic
// let oob = &v[99];
}1.2.2 通过 get 获取
get 方法会返回一个 Option<&T>(或 Option<&mut T>),通过 Option 中的 None 枚举显式处理元素不存在的情况;相比索引更安全,但写法略长。
注
get 方法通常获取只读引用,若需修改元素则用 get_mut
fn main() {
let v = vec![1, 2, 3, 4, 5];
let third: Option<&i32> = v.get(2);
match third {
Some(third) =>
println!("The third element by get is {}", third),
None => println!("There is no third element."),
}
}fn main() {
let mut v2 = vec![10, 20, 30];
if let Some(x) = v2.get_mut(0) {
*x += 1;
}
println!("v2 after get_mut: {v2:?}");
}1.3 增删元素
常用的增删操作都在尾部或指定位置进行:push 追加,pop 弹出;insert/remove 针对中间元素,clear 清空但保留容量
重要
pop:空 Vec 返回Nonepush:容量不足会自动扩容并搬移已有元素insert/remove:会移动后续所有元素,位置越靠前开销越大。
// push:尾部追加元素,必要时自动扩容
fn main() {
let mut v = Vec::new();
v.push(5);
v.push(6);
v.push(7);
v.push(8);
println!("{v:?}");
}// pop:移除末尾并返回 Option<T>,为空时返回 None
fn main() {
let mut v = vec![1, 2, 3];
let last = v.pop();
println!("{v:?}, last = {last:?}");
}// insert:在指定位置插入,移动之后的所有元素
fn main() {
let mut v = vec![1, 3];
v.insert(1, 2); // [1, 2, 3]
println!("{v:?}");
}// remove:移除指定位置元素,移动其后的元素
fn main() {
let mut v = vec![1, 2, 3];
let removed = v.remove(1); // removes 2 -> [1, 3]
println!("{v:?}, removed = {removed}");
}// clear:清空元素但保留当前容量
fn main() {
let mut v = vec![1, 2, 3];
v.clear();
println!("{v:?}, len = {}, cap = {}", v.len(), v.capacity());
}1.4 遍历 Vec
如果想要依次访问 vector 中的每一个元素,可以使用 for 循环来遍历这个 vector。根据需要选择按值、按引用或按可变引用遍历,决定所有权去向以及是否修改元素
fn main() {
let v = vec![100, 32, 57];
for i in &v {
println!("{i}");
}
}fn main() {
let mut v = vec![100, 32, 57];
for i in &mut v {
*i += 50;
}
}1.5 长度与容量
len() 返回当前元素个数,capacity() 返回已分配的槽位数。可用 reserve/reserve_exact 预留空间,shrink_to_fit/shrink_to 释放多余容量;提前预留能减少多次搬移,收缩能回收未用内存。
fn main() {
let mut v = Vec::with_capacity(2);
v.push(1);
v.push(2);
println!("len {}, cap {}", v.len(), v.capacity()); // 2, 2
v.reserve(10); // 预留更多容量
println!("cap after reserve {}", v.capacity());
v.clear();
v.shrink_to_fit(); // 收缩容量
}2. 借用检查规则
借用检查器会确保对元素的引用与对 Vec 本身的修改不会冲突:只要有对元素的借用,就不能在借用存活期内对 Vec 增删元素。这样可以避免迭代时移动数据导致的悬垂引用。
fn main() {
let mut v = vec![1, 2, 3, 4, 5];
let first = &v[0];
// 在借用元素期间修改 Vec 会报错
// v.push(6);
println!("The first element is: {first}");
}3. 使用枚举来存储多种类型
由于 vector 只能存储相同类型的元素,那么要实现存储多种数据类型的能力就需要借用枚举的能力
enum SpreadsheetCell {
Int(i32),
Float(f64),
Text(String),
}
fn main() {
let row: Vec<SpreadsheetCell> = vec![
SpreadsheetCell::Int(3),
SpreadsheetCell::Text(String::from("blue")),
SpreadsheetCell::Float(10.12),
];
}实际使用时需要用模式匹配区分变体:
匹配枚举变体
fn main() {
let row: Vec<SpreadsheetCell> = vec![
SpreadsheetCell::Int(3),
SpreadsheetCell::Text(String::from("blue")),
SpreadsheetCell::Float(10.12),
];
for cell in row {
match cell {
SpreadsheetCell::Int(i) => println!("int: {i}"),
SpreadsheetCell::Float(f) => println!("float: {f}"),
SpreadsheetCell::Text(s) => println!("text: {s}"),
}
}
}注
如果只是想让不同类型共享同一组行为,也可以用 trait 对象(如 Vec<Box<dyn Trait>>),但需要动态分发;枚举方式则有穷尽性检查且更高效。