文章目录
-
- 一、回顾与导入
- [二、创建 Vec](#二、创建 Vec)
-
- [2.1 使用 `Vec::new()`](#2.1 使用
Vec::new()) - [2.2 使用 `vec!` 宏(最常用)](#2.2 使用
vec!宏(最常用)) - [2.3 使用 `with_capacity()` 预分配容量](#2.3 使用
with_capacity()预分配容量)
- [2.1 使用 `Vec::new()`](#2.1 使用
- 三、读取元素
-
- [3.1 索引语法 `\[\]`](#3.1 索引语法
[]) - [3.2 `get` 方法 ------ 安全访问](#3.2
get方法 —— 安全访问) - [3.3 索引 vs get 对比](#3.3 索引 vs get 对比)
- [3.1 索引语法 `\[\]`](#3.1 索引语法
- [四、修改 Vec](#四、修改 Vec)
-
- [4.1 添加元素:`push`](#4.1 添加元素:
push) - [4.2 删除元素:`pop`](#4.2 删除元素:
pop) - [4.3 插入与删除](#4.3 插入与删除)
- [4.4 修改指定位置](#4.4 修改指定位置)
- [4.1 添加元素:`push`](#4.1 添加元素:
- [五、遍历 Vec](#五、遍历 Vec)
-
- [5.1 不可变遍历](#5.1 不可变遍历)
- [5.2 可变遍历](#5.2 可变遍历)
- 六、常用方法
- 七、所有权与借用
-
- [7.1 存储非 Copy 类型](#7.1 存储非 Copy 类型)
- [7.2 引用类型存储](#7.2 引用类型存储)
- [7.3 遍历时不能修改](#7.3 遍历时不能修改)
- 八、完整代码示例
- 九、常见陷阱与最佳实践
- 十、总结与速查
- 十一、思考题
- 参考链接
掌握 Rust 最常用的动态数组 ------ 创建、增删改查、遍历与常用方法
一、回顾与导入
前几篇文章我们学习了 Rust 的核心概念:所有权、借用、结构体、枚举、模块系统。
现在开始学习 Rust 标准库提供的常用集合类型。这些类型存储在堆上,可以在运行时动态增长。
今天的主角是 Vec<T>(Vector,动态数组),这是 Rust 中最常用的集合类型。
💡 与数组
[T; N]不同,Vec的长度可以在运行时变化。
二、创建 Vec
2.1 使用 Vec::new()
rust
fn main() {
let v: Vec<i32> = Vec::new(); // 需要显式标注类型
println!("长度: {}, 容量: {}", v.len(), v.capacity());
}
2.2 使用 vec! 宏(最常用)
rust
fn main() {
let v = vec![1, 2, 3]; // 类型自动推断为 Vec<i32>
let v2 = vec![0; 5]; // 创建包含 5 个 0 的 Vec
let v3 = vec!["hello", "world"];
println!("v: {:?}", v); // [1, 2, 3]
println!("v2: {:?}", v2); // [0, 0, 0, 0, 0]
}
2.3 使用 with_capacity() 预分配容量
rust
fn main() {
// 预分配容量,避免多次重新分配
let mut v: Vec<i32> = Vec::with_capacity(10);
println!("长度: {}, 容量: {}", v.len(), v.capacity());
for i in 0..5 {
v.push(i);
}
println!("加入5个元素后 - 长度: {}, 容量: {}", v.len(), v.capacity());
}
三、读取元素
3.1 索引语法 []
rust
fn main() {
let v = vec![1, 2, 3, 4, 5];
let first = v[0];
let second = v[1];
println!("第一个元素: {}, 第二个元素: {}", first, second);
}
⚠️ 越界访问会 panic:
rust
fn main() {
let v = vec![1, 2, 3];
// let invalid = v[99]; // 💥 panic! index out of bounds
}
3.2 get 方法 ------ 安全访问
get 返回 Option<&T>,不会 panic:
rust
fn main() {
let v = vec![1, 2, 3];
match v.get(2) {
Some(third) => println!("第三个元素: {}", third),
None => println!("索引越界"),
}
match v.get(99) {
Some(value) => println!("{}", value),
None => println!("索引 99 越界"), // 走到这里
}
}
3.3 索引 vs get 对比
| 方式 | 返回值 | 越界行为 | 适用场景 |
|---|---|---|---|
v[index] |
T(复制)或 &T |
panic | 确定索引有效时 |
v.get(index) |
Option<&T> |
返回 None |
不确定索引是否有效 |
四、修改 Vec
4.1 添加元素:push
rust
fn main() {
let mut v = vec![1, 2, 3];
v.push(4);
v.push(5);
println!("{:?}", v); // [1, 2, 3, 4, 5]
}
4.2 删除元素:pop
rust
fn main() {
let mut v = vec![1, 2, 3];
let last = v.pop(); // 返回 Option<T>
println!("弹出: {:?}, 剩余: {:?}", last, v); // Some(3), [1, 2]
let none = v.pop(); // Some(2)
let none = v.pop(); // Some(1)
let none = v.pop(); // None
println!("弹出空 Vec: {:?}", none);
}
4.3 插入与删除
rust
fn main() {
let mut v = vec![1, 2, 4, 5];
// 在索引 2 处插入 3
v.insert(2, 3);
println!("插入后: {:?}", v); // [1, 2, 3, 4, 5]
// 删除索引 1 处的元素
let removed = v.remove(1);
println!("删除元素: {}, 结果: {:?}", removed, v); // 2, [1, 3, 4, 5]
}
4.4 修改指定位置
rust
fn main() {
let mut v = vec![1, 2, 3];
v[0] = 10;
v[1] = 20;
v[2] = 30;
println!("{:?}", v); // [10, 20, 30]
}
五、遍历 Vec
5.1 不可变遍历
rust
fn main() {
let v = vec![1, 2, 3, 4, 5];
// 方式1:直接遍历元素
for item in &v {
println!("{}", item);
}
// 方式2:带索引遍历
for (i, item) in v.iter().enumerate() {
println!("v[{}] = {}", i, item);
}
}
5.2 可变遍历
rust
fn main() {
let mut v = vec![1, 2, 3, 4, 5];
// 遍历并修改每个元素
for item in &mut v {
*item *= 2; // 解引用后修改
}
println!("{:?}", v); // [2, 4, 6, 8, 10]
}
六、常用方法
| 方法 | 说明 | 示例 |
|---|---|---|
len() |
返回长度 | v.len() |
is_empty() |
是否为空 | v.is_empty() |
capacity() |
返回容量 | v.capacity() |
contains(&item) |
是否包含元素 | v.contains(&3) |
first() / last() |
首/最后一个元素 | v.first() |
clear() |
清空所有元素 | v.clear() |
truncate(len) |
截断到指定长度 | v.truncate(3) |
reserve(n) |
预分配容量 | v.reserve(10) |
rust
fn main() {
let mut v = vec![10, 20, 30, 40, 50];
println!("长度: {}", v.len()); // 5
println!("是否为空: {}", v.is_empty()); // false
println!("包含 30: {}", v.contains(&30)); // true
if let Some(first) = v.first() {
println!("第一个元素: {}", first); // 10
}
if let Some(last) = v.last() {
println!("最后一个元素: {}", last); // 50
}
v.truncate(3);
println!("截断后: {:?}", v); // [10, 20, 30]
v.clear();
println!("清空后: {:?}, 长度: {}", v, v.len()); // [], 0
}
七、所有权与借用
7.1 存储非 Copy 类型
rust
fn main() {
let mut v = Vec::new();
let s1 = String::from("hello");
let s2 = String::from("world");
v.push(s1); // s1 的所有权移动到 v
v.push(s2); // s2 的所有权移动到 v
// println!("{}", s1); // ❌ 编译错误:s1 已移动
println!("{:?}", v); // ["hello", "world"]
}
7.2 引用类型存储
rust
fn main() {
let s = String::from("hello");
let v: Vec<&String> = vec![&s]; // 存储引用
println!("{}", s); // s 仍然可用
println!("{}", v[0]);
}
7.3 遍历时不能修改
rust
fn main() {
let mut v = vec![1, 2, 3];
// ❌ 错误:不能在遍历时修改 Vec
// for item in &v {
// v.push(4);
// }
// ✅ 正确:先收集,后修改
let new_items: Vec<i32> = (4..=6).collect();
v.extend(new_items);
println!("{:?}", v);
}
八、完整代码示例
rust
fn main() {
println!("=== 1. 创建 Vec ===");
let v1 = vec![1, 2, 3];
let v2 = vec![0; 5];
let v3 = Vec::<i32>::new();
let v4: Vec<i32> = (1..=5).collect(); // 从迭代器创建
println!("v1: {:?}", v1);
println!("v2: {:?}", v2);
println!("v4: {:?}", v4);
println!("\n=== 2. 添加和删除 ===");
let mut stack = Vec::new();
stack.push(1);
stack.push(2);
stack.push(3);
println!("栈: {:?}", stack);
while let Some(top) = stack.pop() {
println!("弹出: {}", top);
}
println!("\n=== 3. 安全索引 ===");
let v = vec![10, 20, 30];
for i in 0..=v.len() {
match v.get(i) {
Some(val) => println!("v[{}] = {}", i, val),
None => println!("v[{}] 不存在", i),
}
}
println!("\n=== 4. 遍历与修改 ===");
let mut numbers = vec![1, 2, 3, 4, 5];
for num in &mut numbers {
*num *= *num; // 平方
}
println!("平方后: {:?}", numbers);
println!("\n=== 5. 常用方法 ===");
let mut fruits = vec!["apple", "banana", "orange"];
println!("是否有 banana: {}", fruits.contains(&"banana"));
println!("第一个: {:?}", fruits.first());
println!("最后一个: {:?}", fruits.last());
fruits.insert(1, "grape");
println!("插入后: {:?}", fruits);
let removed = fruits.remove(2);
println!("删除 {} 后: {:?}", removed, fruits);
println!("\n=== 6. 排序 ===");
let mut nums = vec![5, 2, 8, 1, 9];
nums.sort();
println!("升序: {:?}", nums);
nums.sort_by(|a, b| b.cmp(a)); // 降序
println!("降序: {:?}", nums);
println!("\n=== 7. 过滤与映射 ===");
let nums = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let even: Vec<_> = nums.iter().filter(|&&x| x % 2 == 0).collect();
let doubled: Vec<_> = nums.iter().map(|&x| x * 2).collect();
println!("偶数: {:?}", even);
println!("加倍: {:?}", doubled);
}
九、常见陷阱与最佳实践
陷阱1:索引越界
rust
let v = vec![1, 2, 3];
println!("{}", v[3]); // 💥 panic!
✅ 解决 :使用 get() 进行安全检查
陷阱2:遍历时修改
rust
let mut v = vec![1, 2, 3];
for item in &v {
v.push(4); // ❌ 不能修改
}
陷阱3:引用失效
rust
let mut v = vec![1, 2, 3];
let first = &v[0]; // 获取引用
v.push(4); // 可能触发重新分配
println!("{}", first); // ❌ 风险:引用可能失效
性能最佳实践
| 场景 | 推荐做法 |
|---|---|
| 已知大致元素数量 | 使用 Vec::with_capacity() 预分配 |
| 频繁在头部插入/删除 | 考虑 VecDeque |
| 需要去重 | 考虑 HashSet |
| 需要快速查找 | 考虑 HashMap 或二分查找 |
十、总结与速查
| 操作 | 代码 | 时间复杂度 |
|---|---|---|
| 创建空 Vec | Vec::new() |
O(1) |
| 创建带初值 | vec![1, 2, 3] |
O(n) |
| 尾部添加 | v.push(x) |
均摊 O(1) |
| 尾部删除 | v.pop() |
O(1) |
| 索引访问 | v[i] |
O(1) |
| 安全访问 | v.get(i) |
O(1) |
| 插入 | v.insert(i, x) |
O(n) |
| 删除 | v.remove(i) |
O(n) |
| 遍历 | for x in &v |
O(n) |
| 长度 | v.len() |
O(1) |
| 清空 | v.clear() |
O(n) |
核心要点:
- ✅
Vec是最常用的动态数组,长度可变 - ✅ 使用
vec![]宏创建最方便 - ✅ 优先使用
get()安全访问,避免 panic - ✅ 遍历时不能修改,需要修改可先收集再扩展
- ✅ 预分配容量可以提升性能
十一、思考题
- 下面代码会输出什么?是否会 panic?
rust
fn main() {
let v = vec![1, 2, 3];
println!("{}", v.get(3).unwrap_or(&0));
}
-
Vec::new()和Vec::with_capacity(10)有什么区别? -
为什么在遍历 Vec 时不能修改它?即使不会导致重新分配也不行?