Rust 集合:Vec 与 HashMap
Vec 动态数组与 HashMap 哈希表的所有权接口、常用操作、迭代与工程注意点。
#type / concept
#status / growing
#tech / dev
#resource / rust
[!info] 关联笔记
Rust 集合:Vec 与 HashMap
这个概念为什么出现
固定数组不够用时,需要:
- 可增长列表:
Vec<T> - 键值索引:
HashMap<K,V>
它们是 std 集合的主干,接口全面贯彻所有权与借用。
[!abstract] 一句话理解
Vec<T>是堆上连续可增长数组;HashMap<K,V>是哈希键值表;索引/插入/删除的 API 通过所有权与借用表达“拿走还是看一眼”。
最小可运行示例
场景:购物车 SKU 列表 + 优惠券核销表
结算前:购物车是 Vec;已核销优惠券码放在 HashMap 防重复。
use std::collections::HashMap;
// 业务意图:维护购物车行项目与优惠券占用。
// 教学点:Vec push/iter;HashMap entry;所有权 insert。
fn main() {
let mut cart: Vec<String> = Vec::new();
cart.push(String::from("sku-apple"));
cart.push(String::from("sku-milk"));
let mut redeemed: HashMap<String, u32> = HashMap::new();
// entry API:不存在则插入
redeemed.entry(String::from("OFF10")).or_insert(1);
println!("cart_len={}", cart.len());
for sku in &cart {
// 借用迭代,不 move 出 Vec
println!("sku={sku}");
}
println!("off10={}", redeemed.get("OFF10").unwrap());
}
建议运行:cargo run
期望输出:
cart_len=2
sku=sku-apple
sku=sku-milk
off10=1
结合场景再看三个关注点
for sku in &cart避免搬空购物车entry优雅处理“首次插入”- 键的所有权:
insert吃掉 key
核心概念与准确模型
Vec
push/pop/len/capacity- 索引
v[i]越界 panic;get返回Option - 可
as_slice()变成&[T]
HashMap
- 键需实现
Eq + Hash get返回Option<&V>- 迭代顺序默认不保证(除非用特殊 map)
所有权模式
| API | 典型效果 |
|---|---|
push(T) | 拿走 T |
iter() | 借 &T |
into_iter() | 拿走元素 |
remove | 返回拥有的值 |
设计动机
- 动态集合是系统编程刚需
- API 与借用检查一致,避免迭代时修改等未定义行为
- 与迭代器生态深度整合
边界与误区
- 迭代
Vec时不要同时push同一向量(规则/编译或逻辑问题) - HashMap 查找用借用键:
map.get("OFF10")可因Borrow工作 - 预分配
with_capacity降低扩容
[!warning] 常见误区:
for x in vec之后还想用 vecfor x in vec是into_iter,会搬走;用for x in &vec。
工程实践
- 热路径预估 capacity
- 公共 API 返回切片/迭代器而不是强制
Vec若只需读 - 键类型稳定且哈希成本可控
- 需要排序映射用
BTreeMap
本节总结
- Vec 管列表,HashMap 管关联
- 迭代方式决定是否 move
- entry API 处理占位插入
自测题
v.get(i)与v[i]差别?- 为什么 HashMap 键常要拥有?
参考答案
get返回Option,[]越界 panic。- map 需要在自己生命周期内持有键;也可用引用键但需生命周期。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| The Book — Common Collections | 官方书 | Vec/String/HashMap |
| std::vec::Vec | 标准库 | Vec |
| std::collections::HashMap | 标准库 | HashMap |