Rust基础——常用集合
开始
本章节介绍rust中的三个常用集合
- vector
- String
- HashMap
三种集合在内存分配上遵循相同的模式,即在栈上保存一个固定大小的结构体,其中包含一个指向存储元素的堆内存的指针,直接赋值时发生所有权移动
vector
Vector表示一个长度可变的数组,是一个类型为Vec<T>
的结构体
创建vector
使用new()
关联函数创建一个空的vector
1
let v: Vec<i32> = Vec::new();
使用初始值创建vector时,使用vec!
宏,支持类型推断
1
let v = vec![1, 2, 3];
访问vector
有两种方式可以访问vector元素
索引访问:索引越界时会抛出panic
1 2 3 4 5 6 7
let v = vec![1, 2, 3]; let a: i32 = v[0]; // 发生数据移动或拷贝 let b: &i32 = &v[0]; // 获取元素的不可变引用,遵循借用期规则 let s: &[i32] = &v[0..2]; // slice引用 let mut v1 = vec![1, 2, 3]; let c: &mut i32 = &mut v1[0]; // 获取元素的可变引用,遵循借用期规则
注意,访问时应该使用引用,直接访问会发生数据的移动或拷贝
get
方法:返回Option<T>
,数组越界时返回None
1 2 3 4 5 6
let v = vec![1, 2, 3]; let a = v.get(0); match a { Some(n) => {} None => {} }
更新vector
使用更新vector的方法时,会隐式传入vector的可变引用,因此需要声明变量为可变的
添加元素
1 2
let mut v = vec![1, 2, 3]; v.push(1); // 添加元素
修改元素
1 2 3 4 5 6
let mut v = vec![1, 2, 3]; v[0] = 1; // 通过引用修改 let a: &mut i32 = &mut v[0]; // 获取可变引用 *a = 2; // 解引用后获取引用的值,可访问或修改
删除元素
1 2
let mut v = vec![1, 2, 3]; let removed = v.remove(0); // 移除第一个元素并返回它
遍历vector
不可变引用遍历
1
2
3
4
let v = vec![100, 32, 57];
for i in &v {
println!("{i}");
}
可变引用遍历
1
2
3
4
let mut v = vec![100, 32, 57];
for i in &mut v {
*i += 50;
}
String
创建字符串
1
2
let empty = String::new(); // 创建一个空字符串
let from = String::from("hello"); // 使用字面量创建字符串对象
添加字符串
1
2
3
4
5
6
7
8
9
let s = String::new();
let s1 = String::from("world");
// push_str接收一个&str类型参数
s.push_str("hello"); // 添加字符串字面量
s.push_str(&s1); // 附加其他字符串,不移动所有权
// push接受一个char类型参数
s.push('a');
拼接字符串
1
2
3
4
5
6
7
8
9
10
// 使用+运算符拼接两个字符串
let s1 = String::from("Hello, ");
let s2 = String::from("world!");
let s3 = s1 + &s2; // 注意s1被移动了,不能继续使用
// 使用format!宏拼接字符串
let s1 = String::from("tic");
let s2 = String::from("tac");
let s3 = String::from("toe");
let s = format!("{s1}-{s2}-{s3}"); // 传入引用,不移动所有权
注意,使用
+
运算符实际是调用了add
方法,它的函数签名为fn add(self, s: &str) -> String
,左操作数传入值,会发生所有权移动,右操作数传入&str
引用,可以接收String引用和字符串字面量
遍历字符串
由于rust内对于存储的字符串字节支持多种方式来解释(字节、Unicode标量值等),因此字符串不支持索引访问
遍历字符串需要使用字符数组或字节数组的迭代器访问
1
2
3
4
5
6
7
8
9
// 字符数组迭代器
for c in "hello".chars() {
println!("{c}");
}
// 字节数组迭代器
for c in "hello".chars() {
println!("{c}");
}
HashMap
使用HashMap需要从模块中引入
1
use std::collections::HashMap;
创建HashMap
1
2
use std::collections::HashMap;
let map: HashMap<String, i32> = HashMap::new(); // 创建空的HashMap
插入键值对
1
2
let mut map = HashMap::new(); // 可根据插入数据推断类型
map.insert(10, String::from("hello"));
注意,若在插入时传入变量,则会发生数据的移动或拷贝
1
2
3
4
let mut map = HashMap::new();
let key = 10;
let value = String::from("hello");
map.insert(key, value); // key发生拷贝,value发生移动
访问HashMap
1
2
3
4
let mut map = HashMap::new();
map.insert(String::from("hello"), 20);
let key = String::from("hello");
let value: Option<&i32> = map.get(&key); // 传入引用,返回Option<&V>
遍历HashMap
1
2
3
4
5
6
7
8
9
// 通过不可变引用遍历
for (key, value) in &map {
println!("{key}: {value}");
}
// 通过可变引用遍历,value为可变引用
for (key, value) in &mut map {
println!("{key}: {value}");
}
更新HashMap
覆盖旧值
当多次对同一个键调用
insert
方法时,新值会覆盖旧值缺失值插入
使用
entry
方法获取键值对,返回Entry<K, V>
枚举,其中包含Occupied
和Vacant
两种状态Entry
中包含许多原地操作方法,如and_modify
和or_insert
等1 2 3 4
let mut map = HashMap::new(); map.insert(String::from("a"), 1); // 获取键值对后,若为Occupied,返回value可变引用,若为Vacant,则插入指定值,返回value可变引用 let value: &mut i32 = map.entry(String::from("b")).or_insert(1);