集合类型:Rust 中的字符串、数组、向量、哈希映射和集合
Rust 提供了多种集合类型来存储和操作数据。这些集合类型包括字符串(String 和字符串切片)、数组与切片、向量(Vec)、哈希映射(HashMap)和集合(HashSet)。本文将详细介绍这些集合类型的定义、使用和常见操作,并通过完整的代码示例和详尽的指导过程帮助读者深入理解这些概念。
1. 字符串:String 和字符串切片
1.1 String 类型
String 是 Rust 中的动态字符串类型,存储在堆上,可以动态增长。
示例 1:创建和修改 String
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
fn main() {
// 创建一个空的 String
let mut s = String::new();
// 使用初始值创建 String
let s = String::from("hello");
// 修改 String
let mut s = String::from("hello");
s.push_str(", world!"); // 追加字符串
s.push('!'); // 追加字符
println!("{}", s); // 输出:hello, world!!
}
|
解释:
String::new() 创建一个空的 String。
String::from("hello") 从字符串字面量创建 String。
push_str 和 push 方法用于修改 String。
示例 2:字符串拼接
1
2
3
4
5
6
7
|
fn main() {
let s1 = String::from("hello");
let s2 = String::from("world");
let s3 = s1 + &s2; // s1 的所有权被转移
println!("{}", s3); // 输出:helloworld
}
|
解释:
+ 运算符用于拼接字符串,注意 s1 的所有权会被转移。
1.2 字符串切片
字符串切片(&str)是对字符串的引用,通常用于函数参数。
示例 3:使用字符串切片
1
2
3
4
5
6
|
fn main() {
let s = String::from("hello, world");
let slice = &s[0..5]; // 获取字符串切片
println!("{}", slice); // 输出:hello
}
|
解释:
&s[0..5] 获取字符串 s 的前 5 个字符的切片。
- 字符串切片是只读的,不能修改。
2. 数组与切片
2.1 数组
数组是固定长度的集合,所有元素的类型必须相同。
示例 4:定义和使用数组
1
2
3
4
5
6
7
8
|
fn main() {
let a = [1, 2, 3, 4, 5]; // 定义一个数组
let first = a[0]; // 访问数组元素
let second = a[1];
println!("First element: {}", first); // 输出:1
println!("Second element: {}", second); // 输出:2
}
|
解释:
let a = [1, 2, 3, 4, 5]; 定义了一个包含 5 个元素的数组。
- 数组的长度是固定的,不能动态增长。
2.2 切片
切片是对数组或向量的引用,可以动态指定长度。
示例 5:使用切片
1
2
3
4
5
6
|
fn main() {
let a = [1, 2, 3, 4, 5];
let slice = &a[1..3]; // 获取数组切片
println!("Slice: {:?}", slice); // 输出:[2, 3]
}
|
解释:
&a[1..3] 获取数组 a 的索引 1 到 3(不包括 3)的切片。
- 切片是只读的,不能修改。
3. 向量:Vec 类型
向量是动态数组,可以动态增长和缩小。
3.1 创建和修改向量
示例 6:创建和修改向量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
fn main() {
// 创建一个空的向量
let mut v: Vec<i32> = Vec::new();
// 使用初始值创建向量
let v = vec![1, 2, 3];
// 修改向量
let mut v = vec![1, 2, 3];
v.push(4); // 追加元素
v.pop(); // 移除最后一个元素
println!("{:?}", v); // 输出:[1, 2, 3]
}
|
解释:
Vec::new() 创建一个空的向量。
vec![1, 2, 3] 使用宏创建包含初始值的向量。
push 和 pop 方法用于修改向量。
3.2 访问向量元素
示例 7:访问向量元素
1
2
3
4
5
6
7
8
9
|
fn main() {
let v = vec![1, 2, 3];
let first = v[0]; // 通过索引访问
let second = v.get(1); // 通过 get 方法访问
println!("First element: {}", first); // 输出:1
println!("Second element: {:?}", second); // 输出:Some(2)
}
|
解释:
v[0] 通过索引访问向量元素,如果索引越界会导致程序崩溃。
v.get(1) 通过 get 方法访问向量元素,返回 Option 类型。
4. 哈希映射:HashMap
哈希映射是一种键值对集合,允许通过键快速查找值。
4.1 创建和修改哈希映射
示例 8:创建和修改哈希映射
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
use std::collections::HashMap;
fn main() {
// 创建一个空的哈希映射
let mut scores = HashMap::new();
// 插入键值对
scores.insert(String::from("Alice"), 100);
scores.insert(String::from("Bob"), 90);
// 修改值
scores.insert(String::from("Alice"), 95); // 更新值
scores.entry(String::from("Charlie")).or_insert(85); // 如果键不存在,则插入
println!("{:?}", scores); // 输出:{"Alice": 95, "Bob": 90, "Charlie": 85}
}
|
解释:
HashMap::new() 创建一个空的哈希映射。
insert 方法用于插入或更新键值对。
entry 方法用于检查键是否存在,如果不存在则插入。
4.2 访问哈希映射的值
示例 9:访问哈希映射的值
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(String::from("Alice"), 100);
scores.insert(String::from("Bob"), 90);
let alice_score = scores.get(&String::from("Alice")); // 获取值
let bob_score = scores.get(&String::from("Bob"));
println!("Alice's score: {:?}", alice_score); // 输出:Some(100)
println!("Bob's score: {:?}", bob_score); // 输出:Some(90)
}
|
解释:
get 方法用于通过键获取值,返回 Option 类型。
5. 集合:HashSet
集合是一种无序且不重复的元素集合。
5.1 创建和修改集合
示例 10:创建和修改集合
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
use std::collections::HashSet;
fn main() {
// 创建一个空的集合
let mut set = HashSet::new();
// 插入元素
set.insert(1);
set.insert(2);
set.insert(3);
// 检查元素是否存在
if set.contains(&2) {
println!("2 is in the set");
}
// 移除元素
set.remove(&2);
println!("{:?}", set); // 输出:{1, 3}
}
|
解释:
HashSet::new() 创建一个空的集合。
insert 方法用于插入元素。
contains 方法用于检查元素是否存在。
remove 方法用于移除元素。
5.2 集合操作
集合支持并集、交集、差集等操作。
示例 11:集合操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
use std::collections::HashSet;
fn main() {
let set1: HashSet<_> = [1, 2, 3].iter().cloned().collect();
let set2: HashSet<_> = [3, 4, 5].iter().cloned().collect();
// 并集
let union: HashSet<_> = set1.union(&set2).cloned().collect();
println!("Union: {:?}", union); // 输出:{1, 2, 3, 4, 5}
// 交集
let intersection: HashSet<_> = set1.intersection(&set2).cloned().collect();
println!("Intersection: {:?}", intersection); // 输出:{3}
// 差集
let difference: HashSet<_> = set1.difference(&set2).cloned().collect();
println!("Difference: {:?}", difference); // 输出:{1, 2}
}
|
解释:
union 方法返回两个集合的并集。
intersection 方法返回两个集合的交集。
difference 方法返回两个集合的差集。
6. 综合示例
以下是一个综合示例,展示了字符串、数组、向量、哈希映射和集合的结合使用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
use std::collections::{HashMap, HashSet};
fn main() {
// 字符串操作
let mut s = String::from("hello");
s.push_str(", world!");
println!("{}", s); // 输出:hello, world!
// 数组与切片
let a = [1, 2, 3, 4, 5];
let slice = &a[1..3];
println!("Slice: {:?}", slice); // 输出:[2, 3]
// 向量操作
let mut v = vec![1, 2, 3];
v.push(4);
println!("Vector: {:?}", v); // 输出:[1, 2, 3, 4]
// 哈希映射操作
let mut scores = HashMap::new();
scores.insert(String::from("Alice"), 100);
scores.insert(String::from("Bob"), 90);
println!("Scores: {:?}", scores); // 输出:{"Alice": 100, "Bob": 90}
// 集合操作
let mut set1 = HashSet::new();
set1.insert(1);
set1.insert(2);
let mut set2 = HashSet::new();
set2.insert(2);
set2.insert(3);
let intersection: HashSet<_> = set1.intersection(&set2).cloned().collect();
println!("Intersection: {:?}", intersection); // 输出:{2}
}
|
解释:
- 该示例展示了字符串、数组、向量、哈希映射和集合的基本操作。
7. 总结
Rust 提供了多种集合类型来满足不同的需求。字符串(String 和字符串切片)用于处理文本数据,数组和切片用于固定长度的集合,向量(Vec)用于动态数组,哈希映射(HashMap)用于键值对存储,集合(HashSet)用于不重复的元素集合。掌握这些集合类型的使用是编写高效、安全的 Rust 程序的关键。