Vector 向量
Vec<T> 是 Rust 标准库中提供的动态数组,本质是一个 struct,长度可在运行时动态增减,弥补了数组长度固定的限制。
Vec只能储存相同类型的值(可借助枚举间接储存多种类型)- 数据存放在堆上,栈上保存一个超胖指针(ptr + len + cap)
- 整体所有权: 离开作用域时,
Vec及其所有元素都会被释放
rust
pub struct Vec<T> {
ptr: Unique<T>, // 指向堆内存的原始指针 (8字节)
cap: usize, // 容量(当前最多可存放多少元素)(8字节)
len: usize, // 当前元素数量 (8字节)
}新建 vector
| 语法 | 说明 |
|---|---|
Vec::new() | 创建一个空的 Vec,需要显式类型标注 |
Vec::from() | 从数组或切片创建 Vec,可自动推导类型 |
vec![] 宏 | 创建 Vec,可自动推导类型,支持重复元素语法 [value; count] |
Vec::with_capacity() | 预分配容量,适合已知元素数量时使用,避免频繁扩容带来的性能损失 |
rust
// Vec::new 函数 创建空 Vec(需显式类型标注)
let v: Vec<i32> = Vec::new();
// Vec::from 函数
let v: Vec<i32> = Vec::from([1, 2, 3]);
// vec! 宏(自动推导类型)
let v = vec![1, 2, 3];
// vec![value; count]: 创建 count 个相同初始值的 Vec
let v = vec![0; 5]; // [0, 0, 0, 0, 0]
let v = vec!["hi"; 3]; // ["hi", "hi", "hi"]
// Vec::with_capacity: 预分配容量,避免频繁扩容
let mut v: Vec<i32> = Vec::with_capacity(10);
Vec::new()和Vec::with_capacity()需要显式类型标注;
Vec::from和vec!宏可自动推导类型。
vec也可以和数组直接比较:assert_eq!(vec![1,2,3], [1,2,3])。
增加元素
语法: push(value)
push() 方法添加元素到末尾
rust
let mut v = vec![1, 2, 3, 4, 5]; // 必须声明 mut
v.push(6);
push()方法会获得 vector 的可变引用,vector 必须声明为mut
读取元素
语法: [] 或 get(index)
- 使用
[]直接索引读取元素 - 使用
get()方法读取元素
区别:
[]越界会 panic;get()越界返回None
rust
let v = vec![1, 2, 3, 4, 5];
// 使用索引 []
let third: &i32 = &v[2];
println!("The third element is {third}");
// 使用 get() 方法,返回 Option<&T>
let third: Option<&i32> = v.get(2);
match third {
Some(third) => println!("The third element is {third}"),
None => println!("There is no third element."),
}
get()不会获得所有权,只返回不可变引用
遍历 vector 中的元素
- 使用
for循环遍历不可变引用
rust
let v = vec![100, 32, 57];
// 遍历不可变引用
for i in &v {
println!("{i}");
}- 使用
for循环遍历可变引用修改元素
rust
let mut v = vec![100, 32, 57];
// 遍历可变引用,修改元素
for i in &mut v {
*i += 50;
}- 使用
for循环直接获得所有权,消耗 vector
rust
let v = vec![100, 32, 57];
// 使用迭代器方法(获得所有权,v 被移动)
for i in v {
println!("{i}");
}遍历时使用
&v借用不可变引用;使用&mut v借用可变引用;直接使用v会获得所有权
使用枚举来储存多种类型
- vector 只能储存相同类型的值
- 枚举本身就是一个类型,可以在 vector 中储存多种类型的数据
rust
enum SpreadsheetCell {
Int(i32),
Float(f64),
Text(String),
}
let row = vec![
SpreadsheetCell::Int(3),
SpreadsheetCell::Text(String::from("blue")),
SpreadsheetCell::Float(10.12),
];
// 遍历枚举 vector
for cell in &row {
match cell {
SpreadsheetCell::Int(i) => println!("Int: {i}"),
SpreadsheetCell::Float(f) => println!("Float: {f}"),
SpreadsheetCell::Text(s) => println!("Text: {s}"),
}
}vector 本身类型统一为
Vec<SpreadsheetCell>;访问具体值时需用match或if let模式匹配
数组与向量的区别
| 特性 | array | Vec(向量) |
|---|---|---|
| 长度 | 固定 | 动态 |
| 内存分配 | 栈 | 堆 |
| 语法 | [T; n] | Vec<T> |
| 索引 | usize 类型 | usize 类型 |
| 越界行为 | panic | panic |
内存布局与扩容
Vec 在栈上保存一个超胖指针(24 字节),包含三段元数据(各占一个机器字长):
| 字段 | 含义 |
|---|---|
ptr | 指向堆上数据的起始地址 |
len | 当前元素数量 |
cap | 当前容量(最多可存放多少元素,不够时触发扩容) |
扩容机制: 当插入新元素时容量不足,Vec 会申请一块约为原来两倍大小的新堆内存,将原有元素全部拷贝过去,并更新超胖指针的元数据。
Rust 官方文档指出扩容策略并未固定,未来可能调整,但目前实现通常是 2 倍扩容。
rust
let mut v = vec![11, 22, 33];
println!("len={}, cap={}", v.len(), v.capacity()); // len=3, cap=3
v.push(44); // 容量不足,触发扩容(重新分配内存 + 拷贝)
println!("len={}, cap={}", v.len(), v.capacity()); // len=4, cap=6频繁扩容或大量元素拷贝会降低性能,有两种方式减少扩容:
Vec::with_capacity(n): 创建时预先分配 n 个元素的容量reserve(n): 对已有的 Vec 追加至少 n 个空闲容量
rust
let mut v = Vec::with_capacity(3);
v.push(11); v.push(22); v.push(33);
println!("len={}, cap={}", v.len(), v.capacity()); // len=3, cap=3
v.reserve(10); // 追加至少 10 个空闲位置
println!("len={}, cap={}", v.len(), v.capacity()); // len=3, cap=13
v.reserve(5); // 已有 10 个空闲,足够,什么也不做
println!("len={}, cap={}", v.len(), v.capacity()); // len=3, cap=13常用方法
查询方法
rust
let v = vec![1, 2, 3, 4, 5];
v.len(); // 返回元素个数 → usize
v.is_empty(); // 返回是否为空 → bool
v.first(); // 返回首个元素 → Option<&T>(借用不可变引用)
v.last(); // 返回最后一个元素 → Option<&T>(借用不可变引用)
v.contains(&3); // 返回是否包含某值 → bool(借用不可变引用)
v.iter().position(|x| *x == 3); // 返回首个满足条件的索引 → Option<usize>(借用不可变引用)增删方法
rust
let mut v = vec![3, 1, 4, 1, 5, 9];
v.pop(); // 移除并返回最后一个元素 → Option<T>(获得所有权)
v.remove(0); // 移除索引 0 的元素(后续元素左移),返回该元素(获得所有权)
v.insert(1, 10); // 在索引 1 处插入 10(原有元素右移)
v.split_off(2); // 从索引 2 处一分为二,返回后半部分的新 Vec(获得所有权)
v.drain(0..2); // 移除索引 0..2 的元素,返回被移除元素的迭代器(获得所有权)
v.retain(|x| *x > 2); // 只保留满足条件的元素(原地过滤)
v.dedup(); // 去除相邻重复元素(配合 sort 可实现完全去重)
v.truncate(3); // 截断到长度 3,多余元素被删除;若目标长度 ≥ 当前长度则不做任何事
v.clear(); // 清空向量排序
rust
let mut v = vec![3, 1, 4, 1, 5];
v.sort(); // 对实现了 Ord 的类型排序(整数、字符串等)
v.sort_by(|a, b| b.cmp(a)); // 自定义排序(这里是降序)
v.sort_by_key(|k| std::cmp::Reverse(*k)); // 按 key 排序
let mut floats = vec![3.1f64, 1.4, 2.7];
floats.sort_by(|a, b| a.partial_cmp(b).unwrap()); // f64 没实现 Ord,需用 partial_cmp合并与扩展
rust
let mut a = vec![1, 2, 3];
let b = vec![4, 5, 6];
a.extend(b.iter()); // 追加 b 的元素到 a(借用 b 的不可变引用)
a.extend([7, 8, 9]); // 追加数组
a.append(&mut b); // 将 b 的所有元素移动到 a,b 变为空向量(获得 b 的可变引用)容量与预分配
rust
// Vec::with_capacity: 预先分配空间,适合已知元素数量时
let mut v: Vec<i32> = Vec::with_capacity(100);
v.push(1);
println!("len={}, cap={}", v.len(), v.capacity()); // len=1, cap=100
v.reserve(50); // 追加至少 50 个空闲容量(已有 99 个空闲,什么也不做)
v.shrink_to_fit(); // 释放多余容量,仅保留当前元素所需空间