
Vec、HashMap、String 详解:你的数据终于有地方住了
还记得我们之前学的所有权吗?那时候你的数据就像流浪汉,不知道往哪儿放。现在好了,Rust 给你提供了三种"精装房":Vec(数组的升级版)、HashMap(键值对存储)、和 String(可变字符串)。
你是不是也遇到过这种情况:想用数组,但不知道要存多少个元素?想用字典,但 Rust 的数组只能用数字索引?别急,今天这三个家伙就是来拯救你的。
我当初学的时候,看到 Vec<String> 这种写法,心想:这是啥套娃操作?后来才明白,这不就是"装着字符串的动态数组"嘛!
想象你有个书架,一开始只能放 5 本书。但你是个书虫,书越来越多怎么办?Vec 就是个智能书架,书多了它会自动变大!
核心特点:
这就像你的通讯录:名字是键,电话号码是值。想查谁的电话,直接找名字就行,不用从头翻到尾。
核心特点:
还记得 &str 吗?那是个"只看不动"的字符串视图。String 才是真正能修改的字符串,就像可擦写的白板。
核心特点:
fn main() {
// 创建 Vec 的几种方式
let mut nums = Vec::new(); // 空 Vec
let nums2: Vec<i32> = vec![]; // 也是空 Vec
let nums3 = vec![, , , , ]; // 带初始值
// 添加元素
nums.push();
nums.push();
nums.push();
// 访问元素(注意:可能 panic!)
let first = nums[]; // 直接索引
let second = nums.get(); // 安全访问,返回 Option
// 修改元素
nums[] = ;
// 删除元素
let last = nums.pop(); // 删除并返回最后一个
// 长度
println!("长度:{}", nums.len());
// 遍历
for num in &nums {
println!("{}", num);
}
}
use std::collections::HashMap;
fn main() {
// 创建 HashMap
let mut scores = HashMap::new();
// 插入键值对
scores.insert("Alice", );
scores.insert("Bob", );
scores.insert("Charlie", );
// 访问值(可能不存在!)
let alice_score = scores.get("Alice"); // Some(95)
let david_score = scores.get("David"); // None
// 更新值
scores.entry("Alice").and_modify(|e| *e += );
// 如果不存在则插入默认值
scores.entry("David").or_insert();
// 遍历
for (name, score) in &scores {
println!("{}: {}", name, score);
}
// 删除
scores.remove("Bob");
}
fn main() {
// 创建 String
let mut s1 = String::from("Hello");
let s2 = String::new();
// 添加内容
s1.push('!'); // 添加单个字符
s1.push_str(" World"); // 添加字符串
// 拼接
let s3 = format!("{} {}", s1, "Rust"); // 推荐!
// 访问(注意:不能直接用索引!)
let first_char = s1.chars().next();
// 长度
println!("字节数:{}", s1.len());
println!("字符数:{}", s1.chars().count());
// 切片(返回 &str)
let slice = &s1[..];
// 替换
s1.replace_range(.., "Hi");
// 清空
s1.clear();
}
错误 1:Vec 索引越界
fn main() {
let nums = vec![, , ];
let four = nums[]; // ❌ panic! index out of bounds
}
编译器说:
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 3'
正确做法:
let four = nums.get(); // 返回 None,不会 panic
错误 2:HashMap 访问不存在的键
fn main() {
let mut scores = HashMap::new();
scores.insert("Alice", );
let score = scores["Bob"]; // ❌ panic! no entry found
}
正确做法:
let score = scores.get("Bob"); // 返回 Option<&V>
错误 3:String 索引切片(UTF-8 陷阱)
fn main() {
let s = String::from"你好");
let c = s[]; // ❌ 编译错误!
}
编译器说:
the trait `Index<{integer}>` is not implemented for `String`
解释: 中文字符在 UTF-8 中占 3 个字节,直接索引可能切到一半!
正确做法:
let chars: Vec<char> = s.chars().collect();
let first = chars[]; // '你'
fn main() {
let v1 = vec![, , ];
let v2 = v1; // v1 的所有权转移给 v2
// println!("{:?}", v1); // ❌ 编译错误!v1 已失效
}
解决: 用 clone() 或借用
let v2 = v1.clone(); // 深拷贝
// 或者
let v2 = &v1; // 借用
fn main() {
let mut map = HashMap::new();
let key = vec![, , ];
map.insert(key, "value"); // ❌ 编译错误!Vec 没有实现 Hash
}
解决: 用实现了 Hash 的类型做键(String、&str、数字等)
fn main() {
let mut nums = vec![, , ];
for num in &nums {
nums.push(*num); // ❌ 编译错误!不能边遍历边修改
}
}
解决: 先收集要添加的,再统一添加
let to_add: Vec<_> = nums.iter().copied().collect();
for num in to_add {
nums.push(num);
}
use std::collections::HashMap;
fn count_words(text: &str) -> HashMap<String, usize> {
let mut counts = HashMap::new();
for word in text.split_whitespace() {
// 转小写并去除标点
let word = word.to_lowercase()
.trim_matches(|c: char| !c.is_alphabetic())
.to_string();
if !word.is_empty() {
*counts.entry(word).or_insert() += ;
}
}
counts
}
fn main() {
let text = "Rust is great Rust is fast Rust is safe";
let counts = count_words(text);
for (word, count) in &counts {
println!("{}: {}", word, count);
}
}
use std::collections::HashMap;
struct Student {
name: String,
scores: Vec<f64>,
}
fn main() {
let mut students = HashMap::new();
// 添加学生
students.insert("Alice".to_string(), vec![95.0, 87.0, 92.0]);
students.insert("Bob".to_string(), vec![88.0, 91.0, 85.0]);
// 计算平均分
for (name, scores) in &students {
let avg = scores.iter().sum::<f64>() / scores.len() as f64;
println!("{} 的平均分:{:.2}", name, avg);
}
// 添加新成绩
students.entry("Alice".to_string())
.and_modify(|scores| scores.push(98.0));
}
use std::collections::HashMap;
struct Cache<K, V> {
data: HashMap<K, V>,
capacity: usize,
}
impl<K: Eq + std::hash::Hash + Clone, V> Cache<K, V> {
fn new(capacity: usize) -> Self {
Cache {
data: HashMap::new(),
capacity,
}
}
fn get(&self, key: &K) -> Option<&V> {
self.data.get(key)
}
fn put(&mut self, key: K, value: V) {
// 简单实现:满了就清空(实际应该用 LRU)
if self.data.len() >= self.capacity {
self.data.clear();
}
self.data.insert(key, value);
}
}
fn main() {
let mut cache = Cache::new();
cache.put("a", );
cache.put("b", );
cache.put("c", );
println!("{:?}", cache.get(&"a")); // Some(1)
}

format! 拼接get() 而不是直接索引,避免 panic下篇预告: 程序出错了怎么办?难道只能眼睁睁看着它 panic?下篇我们讲讲 Rust 的错误处理机制,让你优雅地处理各种异常情况!