Rust: Rust 向量 Vec:动态数组的增删改查
最后更新:2026-08-26
Vec<T>(向量)是 Rust 标准库中最常用的动态数组——可以在运行时增长或收缩,所有数据在堆上分配。它是 Rust 的"ArrayList"或"Python list"。
如果说数组是"固定大小的储物柜",Vec 就是"可伸缩的仓库"——不够用时自动扩容,不需要你手动管理内存。
1. 你将学到
- 用
vec!宏和Vec::new()创建动态数组 - 用 push/pop 在尾部增删元素
- 用 insert/remove 在中间位置增删
- 理解容量(capacity)和长度(len)的区别
- Vec 的遍历、迭代和与数组的互相转换
2. 一个超市收银员的故事
(1) 痛苦:购物车的大小不知道
Tom 在开发一个超市收银系统,顾客的购物车:
// The problem with arrays -- you must know how many items in advance!
let cart: [&str; 3] = ["milk", "bread", "eggs"];
// Customer says: "Add one more cola"
// Arrays can't grow dynamically -- you need to redeclare...
let mut cart2 = ["milk", "bread", "eggs", "cola"];
// Customer: "Remove eggs, add butter instead"
// Modify every time? This is painful!
数组大小是编译时固定的。但购物车——你不知道顾客会买 3 件还是 30 件。你需要一个运行时动态增长的数据结构。
(2) Rust Vec 的解法
fn main() {
// Start with an empty cart
let mut cart: Vec<&str> = Vec::new();
// Customer adds items one by one
cart.push("milk");
cart.push("bread");
cart.push("eggs");
println!("Cart has {} items: {:?}", cart.len(), cart);
// Customer adds more
cart.push("cola");
println!("Added cola: {:?}", cart);
// Customer removes an item
cart.pop();
println!("Removed last: {:?}", cart);
// Check what's inside
println!("Current cart: {:?}", cart);
}
Vec::new()创建一个空动态数组,push添加元素(自动扩容),pop移除最后一个元素。不需要指定大小——Vec 在堆上自动管理内存。
3. 动态数组 Vec
(1) 概念总览
graph LR
A[Vec<T> dynamic array] --> B[Creation]
A --> C[CRUD operations]
A --> D[Capacity management]
A --> E[Conversion]
B --> B1[Vec::new()]
B --> B2[vec! macro]
B --> B3[collect()]
C --> C1[push / pop]
C --> C2[insert / remove]
C --> C3[index / .get()]
D --> D1[capacity: allocated]
D --> D2[len: actual use]
D --> D3[shrink_to_fit]
E --> E1[From array]
E --> E2[Back to array]
(2) Vec 与数组对比
| 维度 | 数组 [T; N] |
向量 Vec<T> |
|---|---|---|
| 大小 | 编译时固定 | 运行时动态增长 |
| 分配位置 | 栈(通常) | 堆 |
| 扩容 | 不支持 | 自动 2 倍扩容 |
| Api 丰富度 | 有限 | 极丰富 |
| 访问速度 | 极快(栈上连续) | 极快(堆上连续) |
| 适用场景 | 固定大小、栈上分配 | 未知数量、频繁增减 |
| 性能开销 | 零 | push 可能触发 reallocation |
(3) Vec 常用方法速查
| 方法 | 返回类型 | 说明 | 时间复杂度 |
|---|---|---|---|
push(val) |
() |
尾部追加 | O(1) 均摊 |
pop() |
Option<T> |
尾部弹出 | O(1) |
insert(idx, val) |
() |
指定位置插入 | O(n) |
remove(idx) |
T |
指定位置移除 | O(n) |
get(idx) |
Option<&T> |
安全访问 | O(1) |
len() |
usize |
元素数 | O(1) |
capacity() |
usize |
已分配容量 | O(1) |
clear() |
() |
清空 | O(n) |
contains(&val) |
bool |
是否包含 | O(n) |
sort() |
() |
原地排序 | O(n log n) |
dedup() |
() |
去重(需先排序) | O(n) |
retain(f) |
() |
保留满足条件的元素 | O(n) |
shrink_to_fit() |
() |
释放多余容量 | — |
(4) Vec 遍历方式对比
| 方式 | 语法 | 所有权 | Vec 是否可用 |
|---|---|---|---|
| 借用遍历 | for x in &v |
只读引用 | 可用 |
| 可变遍历 | for x in &mut v |
可变引用 | 可用(已改) |
| 消费遍历 | for x in v |
转移所有权 | 不可用 |
| 迭代器 | v.iter() |
只读引用 | 可用 |
| 枚举迭代 | v.iter().enumerate() |
只读引用+索引 | 可用 |
4. Vec 示例
▶ 示例 1:vec! 宏与 push/pop(难度 ⭐)
// ============================================
// Vec: vec! macro, push, pop, len, is_empty
// ============================================
fn main() {
// Method 1: Vec::new()
let mut v1: Vec<i32> = Vec::new();
v1.push(10);
v1.push(20);
v1.push(30);
println!("v1: {:?}, len={}", v1, v1.len());
// Method 2: vec! macro (most common)
let mut v2 = vec!["apple", "banana", "cherry"];
println!("v2: {:?}, len={}", v2, v2.len());
// Method 3: vec! with repeated value
let v3 = vec![0; 5];
println!("v3 (five zeros): {:?}", v3);
// push: add to the end
v2.push("date");
println!("After push: {:?}", v2);
// pop: remove from the end
let last = v2.pop();
println!("Popped: {:?}", last);
println!("After pop: {:?}", v2);
// is_empty
println!("v2 empty? {}", v2.is_empty());
// Access by index
println!("v2[0] = {}", v2[0]);
// len
println!("v2 length: {}", v2.len());
}
输出:
v1: [10, 20, 30], len=3
v2: ["apple", "banana", "cherry"], len=3
v3 (five zeros): [0, 0, 0, 0, 0]
After push: ["apple", "banana", "cherry", "date"]
Popped: Some("date")
After pop: ["apple", "banana", "cherry"]
v2 empty? false
v2[0] = apple
v2 length: 3
vec!宏是最常用的创建方式——vec!["a", "b", "c"]或者vec。push尾部追加,pop尾部弹出(返回Option<T>)。len()获取当前元素数。
▶ 示例 2:insert/remove 与容量管理(难度 ⭐⭐)
// ============================================
// Vec: insert, remove, capacity vs length
// ============================================
fn main() {
let mut cart: Vec<&str> = Vec::with_capacity(3);
// capacity vs length
println!("Initial -- len: {}, cap: {}", cart.len(), cart.capacity());
cart.push("milk");
cart.push("bread");
cart.push("eggs");
println!("After 3 pushes -- len: {}, cap: {}", cart.len(), cart.capacity());
// insert at arbitrary position
cart.insert(1, "cola"); // insert "cola" at index 1
println!("After insert at 1: {:?}", cart);
println!("Now -- len: {}, cap: {}", cart.len(), cart.capacity());
// Capacity may have doubled!
// remove at arbitrary position
let removed = cart.remove(2); // remove element at index 2
println!("Removed: {}", removed);
println!("After remove at 2: {:?}", cart);
// get -- safe access (returns Option<&T>)
match cart.get(0) {
Some(item) => println!("First item: {}", item),
None => println!("Empty cart!"),
}
// Try an out-of-bounds index with get (safe)
match cart.get(99) {
Some(item) => println!("Item at 99: {}", item),
None => println!("Index 99 is out of bounds!"),
}
// shrink_to_fit: reduce capacity to match length
cart.shrink_to_fit();
println!("After shrink -- len: {}, cap: {}", cart.len(), cart.capacity());
}
输出:
Initial -- len: 0, cap: 3
After 3 pushes -- len: 3, cap: 3
After insert at 1: ["milk", "cola", "bread", "eggs"]
Now -- len: 4, cap: 6
Removed: bread
After remove at 2: ["milk", "cola", "eggs"]
First item: milk
Index 99 is out of bounds!
After shrink -- len: 3, cap: 3
insert(idx, val)在指定位置插入(后续元素后移),remove(idx)移除指定位置元素并返回它。capacity是 Vec 已分配的内存容量(可能大于len),shrink_to_fit()释放多余空间。.get()安全访问,越界返回None而非 panic。
▶ 示例 3:Vec 遍历与迭代方法(难度 ⭐⭐)
// ============================================
// Vec: iteration, mutation, and functional methods
// ============================================
fn main() {
let mut prices = vec![29.9, 49.9, 15.5, 99.0, 8.8];
// Method 1: for-in by reference
print!("Prices: ");
for p in &prices {
print!("{:.1} ", p);
}
println!();
// Method 2: mutable iteration (add tax)
for p in &mut prices {
*p *= 1.1; // 10% tax
}
println!("With 10% tax: {:?}", prices);
// Method 3: .iter().enumerate()
for (i, p) in prices.iter().enumerate() {
println!("Item {}: {:.2}", i, p);
}
// Method 4: functional style -- map, filter, sum
let total: f64 = prices.iter().sum();
println!("Total: {:.2}", total);
let above_30: Vec<f64> = prices.iter()
.filter(|&&p| p > 30.0)
.copied()
.collect();
println!("Items above 30: {:?}", above_30);
let discounted: Vec<f64> = prices.iter()
.map(|p| p * 0.8) // 20% off
.collect();
println!("20% discount: {:?}", discounted);
}
输出:
Prices: 29.9 49.9 15.5 99.0 8.8
With 10% tax: [32.89, 54.89, 17.05, 108.9, 9.68]
Item 0: 32.89
Item 1: 54.89
Item 2: 17.05
Item 3: 108.90
Item 4: 9.68
Total: 223.41
Items above 30: [32.89, 54.89, 108.9]
20% discount: [26.312, 43.912, 13.640000000000002, 87.12, 7.744]
Vec 支持多种遍历:
&v只读遍历,&mut v修改元素。函数式风格.iter()、.map()、.filter()、.sum()让数据处理简洁优雅。.collect()将迭代器转回 Vec。
▶ 示例 4:Vec 与数组互相转换(难度 ⭐⭐)
// ============================================
// Conversion between Vec and arrays
// ============================================
fn main() {
// Array -> Vec: via .to_vec()
let arr: [i32; 5] = [10, 20, 30, 40, 50];
let vec_from_arr: Vec<i32> = arr.to_vec();
println!("Array to Vec: {:?}", vec_from_arr);
// Array -> Vec: via .iter().copied().collect()
let vec_via_iter: Vec<i32> = arr.iter().copied().collect();
println!("Via iter: {:?}", vec_via_iter);
// Vec -> Array: via try_into() (returns Result)
let vec_data = vec![1, 2, 3, 4];
// let bad_array: [i32; 5] = vec_data.try_into().unwrap(); // PANIC: length mismatch
let ok_array: [i32; 4] = vec_data.try_into().unwrap();
println!("Vec to array: {:?}", ok_array);
// Vec -> slice (zero-cost, no copy)
let slice: &[i32] = &ok_array[1..3];
println!("Slice of array: {:?}", slice);
// Vec from iterator
let squares: Vec<i32> = (1..=5).map(|x| x * x).collect();
println!("Squares: {:?}", squares);
// Vec from repeated value
let repeated = vec!["hello"; 3];
println!("Repeated: {:?}", repeated);
}
输出:
Array to Vec: [10, 20, 30, 40, 50]
Via iter: [10, 20, 30, 40, 50]
Vec to array: [1, 2, 3, 4]
Slice of array: [20, 30]
Squares: [1, 4, 9, 16, 25]
Repeated: ["hello", "hello", "hello"]
数组转 Vec 用
.to_vec()。Vec 转数组用.try_into().unwrap()——长度必须匹配否则 panic。Vec 可以零成本转换为切片&[T]。从迭代器用.collect()创建 Vec 是常用技巧。
▶ 示例 5:综合练习——购物车与价格计算(难度 ⭐⭐⭐)
// ============================================
// 综合示例:Vec 增删改查 + 函数式迭代
// ============================================
#[derive(Debug, Clone)]
struct Item {
name: String,
price: f64,
quantity: u32,
}
impl Item {
fn new(name: &str, price: f64, quantity: u32) -> Self {
Item { name: name.to_string(), price, quantity }
}
fn subtotal(&self) -> f64 {
self.price * self.quantity as f64
}
}
fn print_cart(cart: &[Item]) {
println!("{:<15} {:>8} {:>6} {:>10}", "商品", "单价", "数量", "小计");
println!("{}", "-".repeat(42));
for item in cart {
println!("{:<15} {:>8.2} {:>6} {:>10.2}",
item.name, item.price, item.quantity, item.subtotal());
}
let total: f64 = cart.iter().map(|i| i.subtotal()).sum();
println!("{}", "-".repeat(42));
println!("{:<15} {:>8} {:>6} {:>10.2}", "合计", "", "", total);
}
fn main() {
let mut cart: Vec<Item> = Vec::new();
cart.push(Item::new("Milk", 5.5, 2));
cart.push(Item::new("Bread", 8.0, 1));
cart.push(Item::new("Eggs", 12.5, 3));
cart.push(Item::new("Cola", 3.0, 4));
println!("=== 购物车 ===");
print_cart(&cart);
cart.retain(|i| i.name != "Cola");
println!("\n--- 移除 Cola 后 ---");
print_cart(&cart);
cart.push(Item::new("Butter", 15.0, 2));
if let Some(eggs) = cart.iter_mut().find(|i| i.name == "Eggs") {
eggs.quantity += 2;
}
println!("\n--- 添加 Butter + 增加 Eggs 数量 ---");
print_cart(&cart);
let total: f64 = cart.iter().map(|i| i.subtotal()).sum();
let tax = total * 0.08;
println!("\n税前: {:.2}, 税(8%): {:.2}, 应付: {:.2}", total, tax, total + tax);
let expensive: Vec<&Item> = cart.iter().filter(|i| i.price > 10.0).collect();
println!("单价 > 10 元的商品: {:?}", expensive.iter().map(|i| &i.name).collect::<Vec<_>>());
}
输出:
=== 购物车 ===
商品 单价 数量 小计
------------------------------------------
Milk 5.50 2 11.00
Bread 8.00 1 8.00
Eggs 12.50 3 37.50
Cola 3.00 4 12.00
------------------------------------------
合计 68.50
--- 移除 Cola 后 ---
商品 单价 数量 小计
------------------------------------------
Milk 5.50 2 11.00
Bread 8.00 1 8.00
Eggs 12.50 3 37.50
------------------------------------------
合计 56.50
--- 添加 Butter + 增加 Eggs 数量 ---
商品 单价 数量 小计
------------------------------------------
Milk 5.50 2 11.00
Bread 8.00 1 8.00
Eggs 12.50 5 62.50
Butter 15.00 2 30.00
------------------------------------------
合计 103.50
税前: 103.50, 税(8%): 8.28, 应付: 111.78
单价 > 10 元的商品: ["Eggs", "Butter"]
此示例综合运用了
push、retain(条件删除)、iter_mut().find()(条件修改)、map/filter/sum(函数式统计)等 Vec 核心操作。retain比remove更适合批量删除。
❓ 常见问题
vec!["a", "b"] 和 vec!["a"; 2] 有什么区别?📖 小节
Vec<T>是堆分配的动态数组,可以在运行时增长和收缩- 创建方式:
Vec::new()、vec![]、Vec::with_capacity(n) - 尾部操作:
push添加(均摊 O(1)),pop移除(O(1)) - 中间操作:
insert/remove(O(n),数据需要移位) - 容量 vs 长度:
capacity是已分配空间,len是实际元素数;shrink_to_fit释放多余内存 - Vec 与数组可互相转换,Vec 可零成本转切片
&[T]
📝 作业
- 难度 ⭐:创建一个
Vec<i32>包含数字 1 到 10,用push添加 11 和 12,然后用pop移除最后 3 个元素,最终打印 Vec。 - 难度 ⭐⭐:编写函数
fn remove_evens(v: &mut Vec<i32>)移除 Vec 中所有偶数,保留奇数。在main中用vec![1, 2, 3, 4, 5, 6, 7, 8]测试,结果应为[1, 3, 5, 7]。 - 难度 ⭐⭐⭐:模拟购物车程序。定义一个
struct Item { name: String, price: f64, quantity: u32 }。创建一个Vec<Item>购物车,实现:添加商品、按名称删除商品、修改数量、打印购物车总价。在main中演示完整流程。