本文介绍了 Rust 中的数组(静态数组和动态数组)和 HashMap
的基本概念、创建方法、操作及其内部机制,包括数组的固定长度与动态扩展、HashMap
的键值对存储与扩容策略。
数组是由多个相同类型的元素组合而成的集合。在 Rust 中,数组主要分为两类:
静态数组的特点类似于一个固定大小的书架,一旦确定其大小,就无法再改变。相比之下,动态数组更像是一个可以随时调整大小的抽屉,可以根据需要扩展或缩小。
// 由编译器推断类型
let a = [10, 20, 30, 40, 50];
// 显式指定类型和长度
let b: [i32; 5] = [10, 20, 30, 40, 50];
// 创建包含相同元素的数组
let c = [7; 5]; // c = [7, 7, 7, 7, 7]
fn main() {
// 静态数组:长度固定,存储在栈上
let a = [5u8; 5]; // a = [5, 5, 5, 5, 5]
// 非基础类型的数组需要使用 `std::array::from_fn` 来初始化
let b: [String; 3] = std::array::from_fn(|_| "hello".to_string());
// b = ["hello", "hello", "hello"]
// 数组的元素访问
let c: [u8; 5] = [15, 25, 35, 45, 55];
let first = c[0]; // first = 15
let second = c[1]; // second = 25
// 访问数组越界元素(会导致编译错误)
// let none_element = c[100];
// 二维数组:每个元素都是一个 `[u8; 5]` 类型的数组
let arrays: [[u8; 5]; 2] = [a, c];
}
动态数组 Vec<T>
是 Rust 中可变长度的集合,允许在运行时动态调整大小。与 String
不同,Vec<T>
是通用的,可存储任意类型的元素。
// 显式声明类型
let v1: Vec<i32> = Vec::new();
// 让编译器推断类型。
let mut v2 = Vec::new();
v2.push(10);
// 使用 vec! 宏创建并初始化
let v3 = vec![10, 20, 30];
// 使用 [初始值;长度] 来创建数组
let v4 = vec![1; 3]; // v4 = [1, 1, 1]
// 使用from语法创建数组
let v5 = Vec::from([1, 1, 1]);
fn main() {
let mut v = vec![1, 2, 3, 4, 5];
// 访问元素
let first: &i32 = &v[0];
println!("第一个元素是 {}", first);
// 通过 get 方法安全访问
if let Some(last) = v.get(4) {
println!("最后一个元素是 {}", last);
}
// 修改元素
for i in &mut v {
*i *= 2;
}
println!("修改后的 v: {:?}", v); // [2, 4, 6, 8, 10]
// 删除元素
v.pop();
println!("删除最后一个元素后: {:?}", v); // [2, 4, 6, 8]
v.remove(1);
println!("删除索引 1 处的元素后: {:?}", v); // [2, 6, 8]
}
fn main() {
let mut v = vec![1, 2, 3, 4];
println!("初始容量: {}", v.capacity());
println!("初始 v[0] 的地址: {:p}", &v[0]);
v.push(5);
println!("添加一个元素后,容量: {}", v.capacity());
println!("添加后 v[0] 的地址: {:p}", &v[0]);
v.push(6);
println!("再次添加元素后,容量: {}", v.capacity());
println!("再次添加后 v[0] 的地址: {:p}", &v[0]);
}
初始时 Vec
的容量可能是 4,存储在堆内存中,而栈上存储着指向堆的指针。当 push(5)
触发扩容时,Vec
会申请更大的空间(通常是当前容量的 2 倍),并将数据复制到新的地址。
HashMap
是键值对(key-value)存储结构,可以通过键快速检索值。Rust 使用散列函数计算键的位置,从而提高查找效率。
// 由于 HashMap 并没有包含在 Rust 的 prelude 库中,所以需要手动引入
use std::collections::HashMap;
fn main() {
// 创建一个HashMap,用于存储学生姓名和他们的年龄
let mut student_ages = HashMap::new();
student_ages.insert("Alice", 20);
student_ages.insert("Bob", 19);
student_ages.insert("Charlie", 21);
// 创建一个指定容量的 HashMap,用于存储学生姓名和他们的身高
// 这样可以预先分配内存,减少后续插入时的内存分配开销
let mut student_heights = HashMap::with_capacity(4);
student_heights.insert("Alice", 165);
student_heights.insert("Bob", 180);
student_heights.insert("Charlie", 175);
student_heights.insert("David", 170);
// 打印 HashMap 的内容
println!("学生年龄: {:?}", student_ages);
println!("学生身高: {:?}", student_heights);
}
use std::collections::HashMap;
fn main() {
// 创建一个包含用户姓名和积分的列表
let user_list = vec![
("Alice", 300),
("Bob", 250),
("Eve", 150),
("Mallory", 50),
];
// 将列表转换为 HashMap
let mut user_map: HashMap<&str, i32> = user_list.into_iter().collect();
println!("用户积分: {:?}", user_map);
// 获取元素
let alice_points = user_map["Alice"];
println!("Alice 的积分: {}", alice_points);
// 使用 get 方法安全获取元素
let alice_points = user_map.get("Alice");
println!("Alice 的积分: {:?}", alice_points);
// 尝试获取不存在的键
let trent_points = user_map.get("Trent");
println!("Trent 的积分: {:?}", trent_points);
// 覆盖已有的值,insert 会返回旧值
let old = user_map.insert("Alice", 400); // 更新 Alice 的积分
assert_eq!(old, Some(300)); // 确认旧值是 300
// 使用 entry 方法插入或获取值
let v = user_map.entry("Trent").or_insert(100); // Trent 不存在,插入 100
assert_eq!(*v, 100);
let v = user_map.entry("Trent").or_insert(200); // Trent 已存在,不会修改
assert_eq!(*v, 100);
// 打印更新后的 HashMap
println!("更新后的用户积分: {:?}", user_map);
}
HashMap
提供 entry().or_insert()
方法,允许在键不存在时插入默认值,而不会覆盖已有值。例如,Trent
不存在时,or_insert(100)
会插入 100
,但后续 or_insert(200)
不会修改已存在的值。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!