8.1 标准集合(Vec、HashMap、String 等)
Rust 提供了一些强大的标准集合类型,用于存储和操作不同类型的数据。常见的标准集合包括 Vec、HashMap、String 等,这些集合是 Rust 标准库中的核心组成部分,能够高效地处理动态数据结构。下面,我们将逐一介绍这些常用的数据结构。
8.1.1 Vec<T>:动态数组
Vec(全称:vector)是 Rust 中最常用的集合类型之一。它是一个动态数组,可以存储任意类型的数据,并且在运行时能够根据需要自动调整大小。Vec 提供了丰富的 API,可以方便地对其进行插入、删除、访问等操作。
创建和初始化 Vec
1
2
3
4
5
6
7
8
9
10
11
|
fn main() {
// 创建一个空的 Vec
let mut v: Vec<i32> = Vec::new();
// 向 Vec 中添加元素
v.push(1);
v.push(2);
v.push(3);
println!("{:?}", v); // 输出: [1, 2, 3]
}
|
Vec::new() 创建了一个空的 Vec,我们可以通过 push 方法向其中添加元素。
- 在 Rust 中,
Vec 是类型安全的,我们在创建时需要指定它所存储的元素类型,如上例中的 i32。
访问 Vec 中的元素
1
2
3
4
5
6
7
8
9
10
11
12
13
|
fn main() {
let v = vec![1, 2, 3, 4, 5];
// 访问第一个元素
let first = v[0];
println!("First element: {}", first); // 输出: First element: 1
// 使用 get 方法进行访问,避免下标越界
match v.get(2) {
Some(value) => println!("Third element: {}", value), // 输出: Third element: 3
None => println!("No such element"),
}
}
|
v[0] 是直接通过下标访问元素的方式。如果访问一个不存在的元素(比如超出索引范围),会发生运行时 panic。
get 方法更安全,它返回一个 Option 类型,可以处理越界的情况,避免程序崩溃。
迭代 Vec 中的元素
1
2
3
4
5
6
7
|
fn main() {
let v = vec![1, 2, 3, 4, 5];
for val in &v {
println!("{}", val); // 输出: 1 2 3 4 5
}
}
|
&v 表示对 Vec 的借用,迭代器会按引用访问元素,而不会消耗 Vec 中的数据。
- 使用
for 循环遍历 Vec 中的元素是最常见的方式。
移除元素
1
2
3
4
5
6
7
8
9
10
11
12
13
|
fn main() {
let mut v = vec![1, 2, 3, 4, 5];
// 移除最后一个元素
let last = v.pop();
println!("Popped: {:?}", last); // 输出: Popped: Some(5)
// 移除并返回指定位置的元素
let removed = v.remove(1); // 移除索引为 1 的元素
println!("Removed: {}", removed); // 输出: Removed: 2
println!("{:?}", v); // 输出: [1, 3, 4]
}
|
pop 方法移除并返回 Vec 中的最后一个元素。
remove 方法移除指定位置的元素,并返回该元素。
8.1.2 HashMap<K, V>:哈希表
HashMap 是一种键值对存储结构,允许你根据键来存储、检索和修改数据。它是通过哈希函数来管理键的映射,因此适合用于查找效率较高的场景。与 Vec 不同,HashMap 的键必须是可哈希的类型(实现了 Hash 特征),值可以是任意类型。
创建和初始化 HashMap
1
2
3
4
5
6
7
8
9
10
11
12
|
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
// 向 HashMap 中插入键值对
scores.insert("Alice", 50);
scores.insert("Bob", 60);
scores.insert("Charlie", 70);
println!("{:?}", scores); // 输出: {"Alice": 50, "Bob": 60, "Charlie": 70}
}
|
HashMap::new() 创建一个空的 HashMap,并通过 insert 方法插入键值对。
访问 HashMap 中的元素
1
2
3
4
5
6
7
8
9
10
11
12
13
|
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert("Alice", 50);
scores.insert("Bob", 60);
// 使用 get 方法访问值
match scores.get("Alice") {
Some(&score) => println!("Alice's score: {}", score), // 输出: Alice's score: 50
None => println!("No score for Alice"),
}
}
|
- 使用
get 方法可以安全地访问 HashMap 中的元素,如果键不存在,返回 None。
遍历 HashMap
1
2
3
4
5
6
7
8
9
10
11
|
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert("Alice", 50);
scores.insert("Bob", 60);
for (key, value) in &scores {
println!("{}: {}", key, value);
}
}
|
for (key, value) in &scores 语法可以遍历 HashMap 中的所有键值对。
修改 HashMap 中的值
1
2
3
4
5
6
7
8
9
10
11
12
13
|
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert("Alice", 50);
// 修改已有键的值
if let Some(score) = scores.get_mut("Alice") {
*score += 10;
}
println!("{:?}", scores); // 输出: {"Alice": 60}
}
|
get_mut 方法返回一个可变引用,我们可以修改 HashMap 中已有键的值。
8.1.3 String:可变字符串
String 是 Rust 中一个用于存储可变字符串的类型,它是堆分配的,可以动态扩展。与字符串字面量(&str)不同,String 是可以改变内容的。
创建和修改 String
1
2
3
4
5
6
7
8
9
|
fn main() {
let mut s = String::from("Hello");
// 向 String 中添加内容
s.push_str(", World!");
s.push('!');
println!("{}", s); // 输出: Hello, World!!
}
|
String::from 用于从一个字符串字面量创建 String,push_str 可以向字符串添加其他内容,push 向字符串添加单个字符。
访问 String 中的字符
1
2
3
4
5
6
7
|
fn main() {
let s = String::from("Hello");
for c in s.chars() {
println!("{}", c);
}
}
|
- 使用
chars 方法可以遍历 String 中的字符(每个字符是一个 Unicode 标量值)。
字符串切片与借用
1
2
3
4
5
6
|
fn main() {
let s = String::from("Hello, World!");
let slice = &s[0..5]; // 获取字符串的切片
println!("{}", slice); // 输出: Hello
}
|
- 字符串切片允许我们从
String 中借用一部分数据,而不需要复制内容。
8.1.4 小结
Vec<T> 是一个动态数组,支持灵活的元素插入、删除和访问操作,适用于存储可变大小的数据。
HashMap<K, V> 是一个哈希表,允许根据键快速查找、插入和删除值,适合用于键值对数据存储。
String 提供了一个堆分配的可变字符串类型,可以方便地处理和修改字符串数据。
这些标准集合类型使得 Rust 在处理常见数据结构时,既高效又安全。在下一节中,我们将深入探讨 Rust 的迭代器和迭代器适配器,使得集合的操作更加灵活和高效。