实战总结
> 综合运用数组与Vec知识,通过实战示例巩固集合操作,识别常见错误与性能优化技巧。
完整示例
示例 1:统计计算
rust
▶ Runfn main() {
let numbers = vec![3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5];
// 总和
let sum: i32 = numbers.iter().sum();
// 平均值
let avg = sum as f64 / numbers.len() as f64;
// 最大值和最小值
let max = numbers.iter().max().unwrap();
let min = numbers.iter().min().unwrap();
// 中位数(需要排序)
let mut sorted = numbers.clone();
sorted.sort();
let mid = sorted.len() / 2;
let median = if sorted.len() % 2 == 0 {
(sorted[mid - 1] + sorted[mid]) as f64 / 2.0
} else {
sorted[mid] as f64
};
// 众数(出现最多的元素)
use std::collections::HashMap;
let mut counts = HashMap::new();
for &num in &numbers {
*counts.entry(num).or_insert(0) += 1;
}
let mode = counts.iter().max_by_key(|&(_, count)| count).unwrap().0;
println!("数据:{:?}", numbers);
println!("总和:{}", sum);
println!("平均值:{:.2}", avg);
println!("最大值:{}", max);
println!("最小值:{}", min);
println!("中位数:{:.2}", median);
println!("众数:{}", mode);
}示例 2:待办事项列表
rust
▶ Runstruct TodoList {
items: Vec<TodoItem>,
}
#[derive(Debug)]
struct TodoItem {
description: String,
completed: bool,
}
impl TodoItem {
fn new(description: String) -> Self {
TodoItem {
description,
completed: false,
}
}
}
impl TodoList {
fn new() -> Self {
TodoList {
items: Vec::new(),
}
}
fn add(&mut self, description: String) {
self.items.push(TodoItem::new(description));
println!("已添加:{}", description);
}
fn complete(&mut self, index: usize) -> Result<(), String> {
if index < self.items.len() {
self.items[index].completed = true;
Ok(())
} else {
Err(format!("索引 {} 不存在", index))
}
}
fn remove(&mut self, index: usize) -> Result<TodoItem, String> {
if index < self.items.len() {
Ok(self.items.remove(index))
} else {
Err(format!("索引 {} 不存在", index))
}
}
fn list(&self) {
if self.items.is_empty() {
println!("待办事项为空");
return;
}
println!("\n待办事项列表:");
println!("{:-<40}", "");
for (i, item) in self.items.iter().enumerate() {
let status = if item.completed { "✓" } else { "○" };
println!("{}. [{}] {}", i + 1, status, item.description);
}
println!("{:-<40}", "");
}
fn pending_count(&self) -> usize {
self.items.iter().filter(|item| !item.completed).count()
}
}
fn main() {
let mut todo = TodoList::new();
todo.add(String::from("学习 Rust"));
todo.add(String::from("写代码"));
todo.add(String::from("跑步"));
todo.add(String::from("读书"));
todo.list();
println!("待完成:{} 项", todo.pending_count());
todo.complete(1).unwrap();
todo.complete(3).unwrap();
println!("\n完成两项后:");
todo.list();
todo.remove(2).unwrap();
println!("\n移除一项后:");
todo.list();
}示例 3:矩阵运算
rust
▶ Runtype Matrix = Vec<Vec<f64>>;
fn create_matrix(rows: usize, cols: usize, value: f64) -> Matrix {
vec![vec![value; cols]; rows]
}
fn identity_matrix(size: usize) -> Matrix {
let mut mat = create_matrix(size, size, 0.0);
for i in 0..size {
mat[i][i] = 1.0;
}
mat
}
fn print_matrix(matrix: &Matrix, name: &str) {
println!("{}:", name);
for row in matrix {
print!(" [");
for (i, val) in row.iter().enumerate() {
if i > 0 {
print!(", ");
}
print!("{:6.2}", val);
}
println!("]");
}
println!();
}
fn matrix_add(a: &Matrix, b: &Matrix) -> Matrix {
a.iter()
.zip(b.iter())
.map(|(row_a, row_b)| {
row_a.iter().zip(row_b.iter()).map(|(&a, &b)| a + b).collect()
})
.collect()
}
fn scalar_multiply(matrix: &Matrix, scalar: f64) -> Matrix {
matrix
.iter()
.map(|row| row.iter().map(|&val| val * scalar).collect())
.collect()
}
fn transpose(matrix: &Matrix) -> Matrix {
if matrix.is_empty() {
return Vec::new();
}
let rows = matrix.len();
let cols = matrix[0].len();
(0..cols)
.map(|j| (0..rows).map(|i| matrix[i][j]).collect())
.collect()
}
fn main() {
// 创建矩阵
let a = vec![
vec![1.0, 2.0, 3.0],
vec![4.0, 5.0, 6.0],
];
let b = vec![
vec![7.0, 8.0, 9.0],
vec![10.0, 11.0, 12.0],
];
print_matrix(&a, "矩阵 A");
print_matrix(&b, "矩阵 B");
// 矩阵加法
let sum = matrix_add(&a, &b);
print_matrix(&sum, "A + B");
// 标量乘法
let scaled = scalar_multiply(&a, 2.0);
print_matrix(&scaled, "A * 2");
// 转置
let transposed = transpose(&a);
print_matrix(&transposed, "A 的转置");
// 单位矩阵
let identity = identity_matrix(4);
print_matrix(&identity, "4x4 单位矩阵");
}示例 4:简单的字符串处理器
rust
▶ Runfn main() {
let text = String::from("Hello, Rust! This is a test.");
// 转换为字符 Vec
let chars: Vec<char> = text.chars().collect();
println!("字符数:{}", chars.len());
// 过滤元音
let vowels: Vec<char> = chars
.iter()
.copied()
.filter(|c| "aeiouAEIOU".contains(*c))
.collect();
println!("元音:{:?}", vowels);
// 转换为大写
let upper: Vec<char> = chars
.iter()
.map(|c| c.to_uppercase().next().unwrap())
.collect();
let upper_string: String = upper.into_iter().collect();
println!("大写:{}", upper_string);
// 分词
let words: Vec<&str> = text.split_whitespace().collect();
println!("单词:{:?}", words);
// 单词长度统计
let word_lengths: Vec<usize> = words.iter().map(|w| w.len()).collect();
println!("单词长度:{:?}", word_lengths);
// 连接字符串
let joined = words.join("-");
println!("连接:{}", joined);
}性能优化技巧
预分配容量
rust
▶ Run// ❌ 低效:可能多次重新分配
fn inefficient() -> Vec<i32> {
let mut v = Vec::new();
for i in 0..1000 {
v.push(i);
}
v
}
// ✅ 高效:预分配容量
fn efficient() -> Vec<i32> {
let mut v = Vec::with_capacity(1000);
for i in 0..1000 {
v.push(i);
}
v
}
// ✅ 使用 collect(通常能推断大小)
fn best() -> Vec<i32> {
(0..1000).collect()
}避免不必要的复制
rust
▶ Runfn main() {
let data = vec![1, 2, 3, 4, 5];
// ❌ 不必要的复制
fn process_owned(v: Vec<i32>) -> i32 {
v.iter().sum()
}
let result = process_owned(data.clone()); // 复制了整个 Vec
// ✅ 使用借用
fn process_borrowed(v: &[i32]) -> i32 {
v.iter().sum()
}
let result = process_borrowed(&data); // 只传递引用
}使用 drain 清空并获取元素
rust
▶ Runfn main() {
let mut v = vec![1, 2, 3, 4, 5];
// ❌ 先迭代再清空(两次遍历)
let sum: i32 = v.iter().sum();
v.clear();
// ✅ 使用 drain(一次遍历并清空)
let sum: i32 = v.drain(..).sum();
// v 现在是空的
}常见错误
错误 1:忘记 mut
rust
▶ Runfn main() {
// ❌ 错误:需要 mut 才能修改
// let v = Vec::new();
// v.push(1);
// ✅ 正确
let mut v = Vec::new();
v.push(1);
// 数组同理
// let arr = [1, 2, 3];
// arr[0] = 10; // ❌ 需要 mut
let mut arr = [1, 2, 3];
arr[0] = 10; // ✅
}错误 2:数组越界
rust
▶ Runfn main() {
let numbers = [1, 2, 3];
// ❌ 会导致 panic
// let value = numbers[10];
// ✅ 使用 get 安全访问
match numbers.get(10) {
Some(v) => println!("{}", v),
None => println!("索引超出范围"),
}
// ✅ 或者先检查长度
let index = 10;
if index < numbers.len() {
println!("{}", numbers[index]);
} else {
println!("索引 {} 超出范围", index);
}
}错误信息:
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 10'错误 3:迭代时修改 Vec
rust
▶ Runfn main() {
let mut v = vec![1, 2, 3];
// ❌ 错误:迭代时不能修改
// for i in &v {
// v.push(*i); // 借用冲突
// }
// ✅ 正确:收集后扩展
let to_add: Vec<i32> = v.iter().copied().collect();
v.extend(to_add);
// ✅ 或使用索引
let len = v.len();
for i in 0..len {
let val = v[i] * 2;
v.push(val);
}
}错误信息:
error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable错误 4:返回局部 Vec 的切片
rust
▶ Run// ❌ 错误:返回悬垂引用
// fn get_slice() -> &[i32] {
// let v = vec![1, 2, 3];
// &v // v 在函数结束时被释放
// }
// ✅ 正确:返回 Vec
fn get_vec() -> Vec<i32> {
vec![1, 2, 3]
}
fn main() {
let v = get_vec();
println!("{:?}", v);
}错误 5:误用 dedup
rust
▶ Runfn main() {
let mut v = vec![3, 1, 2, 1, 3];
// ❌ dedup 只移除连续的重复元素
v.dedup();
println!("{:?}", v); // [3, 1, 2, 1, 3](没有变化!)
// ✅ 正确:先排序再去重
let mut v = vec![3, 1, 2, 1, 3];
v.sort();
v.dedup();
println!("{:?}", v); // [1, 2, 3]
}调试技巧
打印调试信息
rust
▶ Runfn main() {
let v = vec![1, 2, 3, 4, 5];
// 基本调试输出
println!("{:?}", v);
// 美化格式
println!("{:#?}", v);
// 使用 dbg! 宏
let result = dbg!(v.len()) * 2;
// dbg! 返回值的引用,可以用于链式调用
let v = vec![1, 2, 3];
let sum: i32 = dbg!(v).iter().sum();
}检查容量变化
rust
▶ Runfn main() {
let mut v: Vec<i32> = Vec::with_capacity(4);
println!("初始:len={}, cap={}", v.len(), v.capacity());
for i in 0..10 {
v.push(i);
println!("push({}): len={}, cap={}", i, v.len(), v.capacity());
}
}练习
练习 1:基本操作
创建一个 Vec,实现以下功能:
- 添加数字 1-10
- 计算所有偶数的和
- 移除最后一个元素
- 在开头插入 0
- 打印最终结果
练习 2:字符串处理
编写函数,接受字符串 Vec,返回:
- 所有字符串连接成一个大写字符串
- 最长字符串的长度
- 所有字符串的平均长度
练习 3:栈实现
使用 Vec 实现一个简单的栈:
push(item)- 入栈pop()- 出栈peek()- 查看栈顶is_empty()- 判断是否为空
练习 4:向量运算
实现向量(Vec<f64>)的基本运算:
dot_product(a, b)- 点积magnitude(v)- 模长normalize(v)- 归一化add(a, b)- 向量加法
小结
数组与 Vec 对比
| 特性 | 数组 | Vec |
|---|---|---|
| 类型 | [T; N] | Vec<T> |
| 长度 | 固定(编译时) | 可变(运行时) |
| 存储 | 栈上(通常) | 堆上 |
| 大小 | N * sizeof(T) | len * sizeof(T) + 开销 |
| 性能 | 稍快 | 稍慢(但更灵活) |
| 方法 | 较少 | 丰富 |
常用方法速查
| 方法 | 数组 | Vec | 说明 |
|---|---|---|---|
len() | ✅ | ✅ | 获取长度 |
is_empty() | ✅ | ✅ | 判断为空 |
first()/last() | ✅ | ✅ | 首尾元素 |
get(i) | ✅ | ✅ | 安全访问 |
iter()/iter_mut() | ✅ | ✅ | 迭代器 |
push()/pop() | ❌ | ✅ | 末尾操作 |
insert()/remove() | ❌ | ✅ | 插入/删除 |
extend() | ❌ | ✅ | 批量添加 |
clear() | ❌ | ✅ | 清空 |
关键要点
- 数组用于固定大小的集合,存储在栈上,性能最优
- Vec用于动态集合,可自动增长,使用最广泛
- 索引访问会进行边界检查,越界会 panic
- 使用
get()方法可以安全访问,返回Option - Vec 扩容时会重新分配内存,预分配容量可优化性能
- 函数参数优先使用切片
&[T],可同时接受数组和 Vec
练习题
详见:练习题