向量(Vec)
向量(Vector)是 Rust 中最常用的集合类型,它允许你在一个单独的数据结构中储存多个值,这些值在内存中彼此相邻地排列。
创建向量
创建空向量
fn main() {
let v: Vec<i32> = Vec::new();
println!("空向量: {:?}", v);
// 使用 vec! 宏创建
let v2 = vec![1, 2, 3];
println!("有初始值的向量: {:?}", v2);
}
使用 vec! 宏
什么是 vec! 宏?
vec!
是 Rust 标准库提供的一个宏(macro),用于方便地创建向量。宏在 Rust 中用感叹号 !
标识。
宏 vs 函数的区别:
- 函数:
Vec::new()
- 运行时调用 - 宏:
vec![]
- 编译时展开,生成创建向量的代码
fn main() {
// 方法1:使用 Vec::new() 函数
let mut v1 = Vec::new();
v1.push(1);
v1.push(2);
v1.push(3);
// 方法2:使用 vec! 宏(更简洁)
let v2 = vec![1, 2, 3];
// 两种方法创建的向量是相同的
println!("v1: {:?}", v1);
println!("v2: {:?}", v2);
println!("相等吗?{}", v1 == v2); // true
}
vec! 宏的不同用法
fn main() {
// 1. 创建包含指定元素的向量
let v1 = vec![1, 2, 3, 4, 5];
println!("指定元素: {:?}", v1);
// 2. 创建包含相同值的向量
let v2 = vec![0; 5]; // [0, 0, 0, 0, 0]
println!("重复值: {:?}", v2);
let v3 = vec!["hello"; 3]; // ["hello", "hello", "hello"]
println!("重复字符串: {:?}", v3);
// 3. 创建空向量(需要指定类型)
let v4: Vec<i32> = vec![];
println!("空向量: {:?}", v4);
// 4. 混合类型(需要使用枚举或 trait 对象)
let v5 = vec![1, 2, 3, 4, 5];
println!("整数向量: {:?}", v5);
}
vec! 宏的编译时展开
vec!
宏在编译时会被展开成实际的代码:
// 这个宏调用:
let v = vec![1, 2, 3];
// 大致等价于:
let mut v = Vec::new();
v.push(1);
v.push(2);
v.push(3);
// 或者更高效的版本:
let v = {
let mut temp_vec = Vec::with_capacity(3);
temp_vec.push(1);
temp_vec.push(2);
temp_vec.push(3);
temp_vec
};
为什么使用 vec! 宏?
- 简洁性:一行代码创建向量
- 可读性:直观地看到向量的内容
- 效率:编译时优化,可能预分配合适的容量
- 类型推断:通常不需要显式指定类型
fn main() {
// 繁琐的方式
let mut v1 = Vec::new();
v1.push(1);
v1.push(2);
v1.push(3);
v1.push(4);
v1.push(5);
// 简洁的方式
let v2 = vec![1, 2, 3, 4, 5];
// 功能完全相同
assert_eq!(v1, v2);
println!("两个向量相等!");
}
更新向量
添加元素
fn main() {
let mut v = Vec::new();
v.push(5);
v.push(6);
v.push(7);
v.push(8);
println!("向量: {:?}", v);
}
插入和删除
fn main() {
let mut v = vec![1, 2, 3, 4, 5];
// 在指定位置插入
v.insert(2, 10);
println!("插入后: {:?}", v);
// 删除最后一个元素
if let Some(last) = v.pop() {
println!("删除的元素: {}", last);
}
println!("删除后: {:?}", v);
// 删除指定位置的元素
let removed = v.remove(2);
println!("删除的元素: {}", removed);
println!("最终向量: {:?}", v);
}
读取向量元素
使用索引
fn main() {
let v = vec![1, 2, 3, 4, 5];
let third: &i32 = &v[2];
println!("第三个元素: {}", third);
// 使用 get 方法(更安全)
match v.get(2) {
Some(third) => println!("第三个元素: {}", third),
None => println!("没有第三个元素"),
}
// 越界访问的处理
match v.get(100) {
Some(element) => println!("元素: {}", element),
None => println!("索引超出范围"),
}
}
安全访问
fn main() {
let v = vec![1, 2, 3, 4, 5];
// 不安全的访问(可能 panic)
// let does_not_exist = &v[100]; // 这会 panic!
// 安全的访问
let does_not_exist = v.get(100);
match does_not_exist {
Some(value) => println!("值: {}", value),
None => println!("索引不存在"),
}
}
遍历向量
不可变引用遍历
fn main() {
let v = vec![100, 32, 57];
for i in &v {
println!("{}", i);
}
// 使用索引遍历
for (index, value) in v.iter().enumerate() {
println!("索引 {}: 值 {}", index, value);
}
}
可变引用遍历
fn main() {
let mut v = vec![100, 32, 57];
for i in &mut v {
*i += 50;
}
println!("修改后的向量: {:?}", v);
}
获取所有权遍历
fn main() {
let v = vec![100, 32, 57];
for i in v {
println!("{}", i);
}
// println!("{:?}", v); // 错误!v 已经被移动
}
使用枚举来储存多种类型
向量只能储存相同类型的值,但可以使用枚举来储存不同类型的数据:
#[derive(Debug)]
enum SpreadsheetCell {
Int(i32),
Float(f64),
Text(String),
}
fn main() {
let row = vec![
SpreadsheetCell::Int(3),
SpreadsheetCell::Text(String::from("blue")),
SpreadsheetCell::Float(10.12),
];
for cell in &row {
match cell {
SpreadsheetCell::Int(i) => println!("整数: {}", i),
SpreadsheetCell::Float(f) => println!("浮点数: {}", f),
SpreadsheetCell::Text(s) => println!("文本: {}", s),
}
}
}
向量的常用方法
容量和长度
fn main() {
let mut v = Vec::with_capacity(10);
println!("容量: {}, 长度: {}", v.capacity(), v.len());
v.push(1);
v.push(2);
v.push(3);
println!("容量: {}, 长度: {}", v.capacity(), v.len());
// 收缩容量
v.shrink_to_fit();
println!("收缩后容量: {}, 长度: {}", v.capacity(), v.len());
}
查找和过滤
fn main() {
let v = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 查找第一个满足条件的元素
let first_even = v.iter().find(|&&x| x % 2 == 0);
println!("第一个偶数: {:?}", first_even);
// 过滤元素
let evens: Vec<&i32> = v.iter().filter(|&&x| x % 2 == 0).collect();
println!("所有偶数: {:?}", evens);
// 检查是否包含某个值
println!("包含 5: {}", v.contains(&5));
// 查找元素位置
if let Some(pos) = v.iter().position(|&x| x == 7) {
println!("7 的位置: {}", pos);
}
}
排序
fn main() {
let mut v = vec![3, 1, 4, 1, 5, 9, 2, 6];
// 排序
v.sort();
println!("排序后: {:?}", v);
let mut v2 = vec![3, 1, 4, 1, 5, 9, 2, 6];
// 逆序排序
v2.sort_by(|a, b| b.cmp(a));
println!("逆序排序: {:?}", v2);
// 按自定义条件排序
let mut words = vec!["hello", "world", "rust", "programming"];
words.sort_by_key(|word| word.len());
println!("按长度排序: {:?}", words);
}
向量的切片
fn main() {
let v = vec![1, 2, 3, 4, 5];
let slice = &v[1..4];
println!("切片: {:?}", slice);
// 获取前 n 个元素
let first_three = &v[..3];
println!("前三个: {:?}", first_three);
// 获取后 n 个元素
let last_two = &v[3..];
println!("后两个: {:?}", last_two);
}
实际应用示例
学生成绩管理
#[derive(Debug)]
struct Student {
name: String,
scores: Vec<f64>,
}
impl Student {
fn new(name: String) -> Student {
Student {
name,
scores: Vec::new(),
}
}
fn add_score(&mut self, score: f64) {
self.scores.push(score);
}
fn average(&self) -> Option<f64> {
if self.scores.is_empty() {
None
} else {
let sum: f64 = self.scores.iter().sum();
Some(sum / self.scores.len() as f64)
}
}
fn highest_score(&self) -> Option<f64> {
self.scores.iter().max_by(|a, b| a.partial_cmp(b).unwrap()).copied()
}
}
fn main() {
let mut student = Student::new("张三".to_string());
student.add_score(85.5);
student.add_score(92.0);
student.add_score(78.5);
student.add_score(88.0);
println!("学生: {:?}", student);
if let Some(avg) = student.average() {
println!("平均分: {:.2}", avg);
}
if let Some(highest) = student.highest_score() {
println!("最高分: {}", highest);
}
}
购物车
#[derive(Debug, Clone)]
struct Item {
name: String,
price: f64,
quantity: u32,
}
impl Item {
fn new(name: String, price: f64, quantity: u32) -> Item {
Item { name, price, quantity }
}
fn total_price(&self) -> f64 {
self.price * self.quantity as f64
}
}
struct ShoppingCart {
items: Vec<Item>,
}
impl ShoppingCart {
fn new() -> ShoppingCart {
ShoppingCart {
items: Vec::new(),
}
}
fn add_item(&mut self, item: Item) {
// 检查是否已存在相同商品
if let Some(existing) = self.items.iter_mut().find(|i| i.name == item.name) {
existing.quantity += item.quantity;
} else {
self.items.push(item);
}
}
fn remove_item(&mut self, name: &str) -> Option<Item> {
if let Some(pos) = self.items.iter().position(|item| item.name == name) {
Some(self.items.remove(pos))
} else {
None
}
}
fn total_cost(&self) -> f64 {
self.items.iter().map(|item| item.total_price()).sum()
}
fn item_count(&self) -> u32 {
self.items.iter().map(|item| item.quantity).sum()
}
fn display(&self) {
println!("购物车内容:");
for item in &self.items {
println!(" {} x{} - ¥{:.2} (小计: ¥{:.2})",
item.name, item.quantity, item.price, item.total_price());
}
println!("总计: {} 件商品,¥{:.2}", self.item_count(), self.total_cost());
}
}
fn main() {
let mut cart = ShoppingCart::new();
cart.add_item(Item::new("苹果".to_string(), 5.0, 3));
cart.add_item(Item::new("香蕉".to_string(), 3.0, 5));
cart.add_item(Item::new("苹果".to_string(), 5.0, 2)); // 会合并到已有的苹果
cart.display();
// 移除商品
if let Some(removed) = cart.remove_item("香蕉") {
println!("移除了: {:?}", removed);
}
println!("\n移除香蕉后:");
cart.display();
}
性能考虑
预分配容量
fn main() {
// 不好的做法:频繁重新分配
let mut v1 = Vec::new();
for i in 0..1000 {
v1.push(i);
}
// 好的做法:预分配容量
let mut v2 = Vec::with_capacity(1000);
for i in 0..1000 {
v2.push(i);
}
println!("v1 容量: {}, v2 容量: {}", v1.capacity(), v2.capacity());
}
避免不必要的克隆
fn process_numbers(numbers: &[i32]) -> i32 {
numbers.iter().sum()
}
fn main() {
let numbers = vec![1, 2, 3, 4, 5];
// 好的做法:传递切片引用
let sum = process_numbers(&numbers);
println!("总和: {}", sum);
// 仍然可以使用原向量
println!("原向量: {:?}", numbers);
}
最佳实践
- 使用
Vec::with_capacity
当你知道大概需要多少元素时 - 使用
get
方法 进行安全的索引访问 - 优先使用迭代器 而不是索引循环
- 使用切片参数 让函数更灵活
- 考虑使用
drain
来高效地移动元素
fn main() {
// 好的实践示例
let mut numbers = Vec::with_capacity(100);
// 添加数据
for i in 1..=10 {
numbers.push(i);
}
// 安全访问
if let Some(&first) = numbers.get(0) {
println!("第一个数字: {}", first);
}
// 使用迭代器处理数据
let doubled: Vec<i32> = numbers.iter().map(|&x| x * 2).collect();
println!("翻倍后: {:?}", doubled);
// 过滤数据
let evens: Vec<i32> = numbers.into_iter().filter(|&x| x % 2 == 0).collect();
println!("偶数: {:?}", evens);
}