标准库提供了常见的容器。包括向量 ( Vector )、映射( HashMap )、集合( HashSet )。
一、向量Vector
数组有一个缺点,就是它的长度在编译时就确定了,一旦确定就永不可更改。
向量是一个长度可变的数组。向量的存储在堆上,因此长度可变。
Rust在标准库中定义了结构体Vec用于表示向量。
(一)定义向量
一维向量
1.new()创建一个空向量
语法格式
let mut instance_name: Vec<T> = Vec::new();
例子
let v1: Vec<i32> = Vec::new(); // 创建类型为i32的空向量
2.vec!()宏创建向量
这种方式的创建方法类似于数组的语法。
它也有3种创建方式。
(1)创建空向量。需要指定类型。
let mut vec_marco: Vec<i32> = vec![];
(2)指定所有元素。可以省略类型。
let mut vec_marco = vec![1, 2, 3, 4, 5];
(3)指定初始值和长度。可以省略类型。
let mut vec_marco = vec![0; 5]; // 长度为5,元素初始化为0
注意,长度可以是变量,这点与数组不同。
例子
let len = 10;
let zero_vec = vec![0; len];
3.从数组创建向量
let arr = [1, 2, 3, 4, 5];
let v = arr.to_vec();
二维向量
1.使用new()创建空向量。需要指定类型。
let mut instance_name: Vec<Vec<T>> = Vec::new();
例子
let v1: Vec<Vec<i32>> = Vec::new();
2.使用vec!()宏创建向量
(1)创建空向量。需要指定类型。
let mut vec_marco: Vec<Vec<i32>> = vec![];
(2)指定所有元素。可以省略类型。
let mut vec_marco = vec![vec![1, 2, 3, 4, 5], ...];
(3)指定初始值和长度。可以省略类型。
let mut vec_marco = vec![vec![0; 5]; 10];
例子
let width = 4;
let height = 4;
let mut array = vec![vec![0; width]; height];
3.从二维数组创建二维向量
例如
let s = [
[0, 0, 1, 0, 0],
[0, 0, 1, 0, 0],
[0, 0, 1, 0, 0],
[0, 0, 1, 0, 0],
];
let s: Vec<_> = s.iter().map(|&e| e.to_vec()).collect();
(二)使用向量
1.获取长度
len()方法
例子
fn main() {
let v = vec![1, 2, 3];
println!("{}", v.len());
let width = 4;
let height = 4;
let array = vec![vec![0; width]; height];
println!("{} {}", array.len(), array[0].len());
}
运行结果如下
3
4 4
2.添加元素
使用push方法来追加单个元素
实例
fn main() {
let mut vector = vec![1, 2, 4, 8];
vector.push(16);
vector.push(32);
vector.push(64);
println!("{:?}", vector);
let mut arr: Vec<Vec<i32>> = Vec::new();
arr.push(Vec::new());
arr.push(vec![]);
arr[0].push(1);
println!("{:?}", arr);
}
运行结果:
[1, 2, 4, 8, 16, 32, 64]
[[1], []]
使用append方法用于将一个向量拼接到另一个向量的尾部
实例
fn main() {
let mut v1: Vec<i32> = vec![1, 2, 4, 8];
let mut v2: Vec<i32> = vec![16, 32, 64];
v1.append(&mut v2);
println!("{:?}", v1);
}
运行结果:
[1, 2, 4, 8, 16, 32, 64]
2.删除元素
使用remove()删除元素
remove() 方法移除并返回向量中指定的下标索引处的元素,将其后面的所有元素移到向左移动一位。
fn main() {
let mut v = vec![10,20,30];
v.remove(1);
println!("{:?}",v);
}
运行结果如下
[10, 30]
clear
删除所有元素
let mut v = vec![10,20,30];
v.clear();
println!("{:?}",v);
3.获取元素
因为向量实现了Deref Target=[T],所以向量自动实现了切片的所有方法。
(1)使用get方法
get方法根据索引返回对元素或子切片的引用。
如果给定位置,则返回该位置上的元素的引用,如果越界则返回None。
如果给定范围,则返回对应于该范围的子切片,如果越界则返回None。
例子
let v = vec![10, 40, 30];
assert_eq!(Some(&40), v.get(1));
assert_eq!(Some(&[10, 40][..]), v.get(0..2));
assert_eq!(None, v.get(3));
assert_eq!(None, v.get(0..4));
(2)get_mut
get_mut方法根据索引返回对元素或子切片的可变引用
如果给定位置,则返回该位置上的元素的可变引用,如果越界则返回None。
如果给定范围,则返回对应于该范围的可变子切片,如果越界则返回None。
例子
let mut x = vec![0, 1, 2];
if let Some(elem) = x.get_mut(1) {
*elem = 42;
}
assert_eq!(x, &[0, 42, 2]);
(3)使用[]
实例
fn main() {
let v = vec![1, 2, 4, 8];
println!("{}", v[1]);
}
运行结果:
2
二维向量
array[2][2] = 5;
println!("{:?}", array);
例子
use std::io;
fn main(){
let width = 4;
let height = 4;
let mut array = vec![vec![0; width]; height];
for i in 0..4 {
let mut buf = String::from("");
io::stdin().read_line(&mut buf).unwrap();
array[i] = buf.split_whitespace().map(|s| s.parse().unwrap()).collect();
}
println!("{:?}", array);
}
4.遍历向量
(1)使用索引
fn main() {
let v = vec![100, 32, 57];
for i in 0..v.len() {
println!("{}", v[i]);
}
}
(2)使用向量的引用
实例
fn main() {
let v = vec![100, 32, 57];
for i in &v {
println!("{}", i);
}
}
运行结果:
100
32
57
实例
fn main() {
let mut v = vec![100, 32, 57];
for i in &mut v {
*i += 50;
}
println!("{:?}", v);
}
(3)使用迭代器
iter()返回一个只读迭代器
for num in nums1.iter() {
print!("{} ", num);
}
println!();
iter_mut()返回一个可写迭代器
let mut nums1 = [1; 5];
let mut i = 0;
for num in nums1.iter_mut() {
*num = i;
i += 1;
}
println!("{:?}", nums1);
into_iter()返回一个迭代器,但是转让所有权
let mut nums1 = [1; 5];
for num in nums1.into_iter() {
print!("{} ", num);
}
println!();
for num in nums1.into_iter()
实际上等价于
for num in nums1
(4)使用迭代器的enumerate
let mut nums1 = [1; 5];
for (pos, v) in nums1.iter().enumerate() {
println!("nums[{}]={}", pos, v);
}
println!("{:?}", nums1);
for (pos, v) in nums1.iter_mut().enumerate() {
*v=pos;
println!("nums[{}]={}", pos, v);
}
println!("{:?}", nums1);
二维向量
(1)使用索引
let mut v = vec![vec![1, 2, 3, 4, 5], vec![3, 9, 8]];
for i in 0..v.len() {
for j in 0..v[i].len() {
println!("{}", v[i][j]);
}
}
(2)使用引用
例子
let s = [
[0, 0, 1, 0, 0],
[0, 0, 1, 0, 0],
[0, 0, 1, 0, 0],
[0, 0, 1, 0, 0],
];
let mut s: Vec<_> = s.iter().map(|&e| e.to_vec()).collect();
for row in &s{
for col in row{
print!("{} ", col);
}
println!();
}
for row in &mut s {
for col in row {
*col += 1;
print!("{} ", col);
}
println!();
}
(3)使用向量的迭代器
let mut grid = [[0; 5]; 5];
for row in grid.iter() {
for col in row.iter() {
print!("{} ", col);
}
println!();
}
let mut i = 0;
for row in grid.iter_mut(){
for col in row.iter_mut() {
*col = i;
i += 1;
print!("{} ", col);
}
println!();
}
(4)使用迭代器的enumerate
let mut grid = [[0; 5]; 5];
for (i, row) in grid.iter().enumerate() {
for (j, col) in row.iter().enumerate() {
print!("{}", col);
}
println!()
}
for (i, row) in grid.iter_mut().enumerate() {
for (j, col) in row.iter_mut().enumerate() {
*col = 1;
}
}
6.使用contains() 判断向量是否包含某个元素
如果值在向量中存在则返回 true ,否则返回 false 。
fn main() {
let v = vec![10,20,30];
if v.contains(&10) {
println!("found 10");
}
println!("{:?}",v);
}
运行结果如下
found 10
[10, 20, 30]
7.逆转向量
reverse
适当地反转切片中元素的顺序。
例子
let mut v = [1, 2, 3];
v.reverse();
assert!(v == [3, 2, 1]);
二、映射HashMap
映射表(Map)在其他语言中广泛存在。其中应用最普遍的就是散列映射表(Hash Map)。
HashMap就是 键值对 的集合。映射表中不允许有重复的键,但允许不同的键有相同的值。
Rust语言定义了HashMap结构体来表示映射表。
HashMap结构体定义在std::collections模块中,使用之前需要导入std::collections模块。
(一)定义映射表
1.使用new创建空映射表
语法格式如下
let mut instance_name: HashMap<type1, type2> = HashMap::new();
实例
use std::collections::HashMap;
let mut map: HashMap<&str, i32> = HashMap::new();
let mut map = HashMap::new();//可以省略类型
2.可以从数组初始化HashMap
例子
use std::collections::HashMap;
let solar_distance = HashMap::from([
("Mercury", 0.4),
("Venus", 0.7),
("Earth", 1.0),
("Mars", 1.5),
]);
(二)使用映射表
1.使用len()获取键值对的个数
例子
use std::collections::HashMap;
fn main() {
let mut stateCodes = HashMap::new();
stateCodes.insert("name","简单教程");
stateCodes.insert("site","https://www.twle.cn");
println!("size of map is {}",stateCodes.len());
}
编译运行结果如下
size of map is 2
2.添加键值对
使用insert()方法用于插入或更新一个键值对
如果键已经存在,则更新为新的值,并则返回旧的值。
如果键不存在则执行插入操作并返回None 。
例子
fn main() {
let mut map = HashMap::new();
map.insert("color", "red");
map.insert("size", "10 m^2");
println!("{}", map.get("color").unwrap());
}
运行结果:
red
3.删除指定键值对
remove()用于从映射表中删除指定的键值对。
如果键值对存在则返回删除的键值对,返回的数据格式为 (&'a K, &'a V) 。
如果键值对不存在则返回None
例子
use std::collections::HashMap;
fn main() {
let mut stateCodes = HashMap::new();
stateCodes.insert("name","简单教程");
stateCodes.insert("site","https://www.twle.cn");
stateCodes.insert("slogn","简单教程,简单编程");
println!("length of the hashmap {}",stateCodes.len());
stateCodes.remove(&"site");
println!("length of the hashmap after remove() {}",stateCodes.len());
}
编译运行结果如下
length of the hashmap 3
length of the hashmap after remove() 2
clear
清除map,删除所有键值对。
例子
use std::collections::HashMap;
let mut a = HashMap::new();
a.insert(1, "a");
a.clear();
assert!(a.is_empty());
4.根据键获取相应的值
使用get()方法获取键相应的值。
如果值不存在,则返回None 。
如果值存在,则返回值的一个引用。
例子
use std::collections::HashMap;
fn main() {
let mut stateCodes = HashMap::new();
stateCodes.insert("name","简单教程");
stateCodes.insert("site","https://www.twle.cn");
println!("size of map is {}",stateCodes.len());
println!("{:?}",stateCodes);
match stateCodes.get(&"name") {
Some(value)=> {
println!("Value for key name is {}",value);
}
None => {
println!("nothing found");
}
}
}
编译运行结果如下
size of map is 2
{"name": "简单教程", "site": "https://www.twle.cn"}
Value for key name is简单教程
get_mut
实例
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
map.insert(1, "a");
if let Some(x) = map.get_mut(&1) {
*x = "b";
}
}
get_key_value
例子
use std::collections::HashMap;
let mut map = HashMap::new();
map.insert(1, "a");
assert_eq!(map.get_key_value(&1), Some((&1, &"a")));
assert_eq!(map.get_key_value(&2), None);
5.遍历映射表
(1)使用引用
for (book, review) in &book_reviews {
println!("{book}: \"{review}\"");
}
for (book, review) in &mut book_reviews {
*review = 0;
println!("{book}: \"{review}\"");
}
(2)使用迭代器
iter()方法会返回映射表中 键值对的引用组成的无序迭代器。
迭代器元素的类型为 (&'a K, &'a V) 。
实例
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
map.insert("color", "red");
map.insert("size", "10 m^2");
for p in map.iter() {
println!("{:?}", p);
}
}
运行结果:
("color", "red")
("size", "10 m^2")
迭代元素是表示键值对的元组。
iter_mut
例子
use std::collections::HashMap;
let mut map = HashMap::from([
("a", 1),
("b", 2),
("c", 3),
]);
// 更新所有值
for (_, val) in map.iter_mut() {
*val *= 2;
}
6.是否包含指定的键
contains_key()方法用于判断映射表中是否包含指定的键值对。
如果包含指定的键,那么会返回相应的值的引用,否则返回None 。
例子
use std::collections::HashMap;
fn main() {
let mut stateCodes = HashMap::new();
stateCodes.insert("name","简单教程");
stateCodes.insert("site","https://www.twle.cn");
stateCodes.insert("slogn","简单教程,简单编程");
if stateCodes.contains_key(&"name") {
println!("found key");
}
}
编译运行结果如下
found key
7.entry
entry方法返回键值对
例子
use std::collections::HashMap;
let mut player_stats = HashMap::new();
fn random_stat_buff() -> u32 {
42
}
player_stats.entry("health").or_insert(100); // 没有就插入,有就跳过
player_stats.entry("defence").or_insert_with(random_stat_buff); //没有就插入,有就跳过
let stat = player_stats.entry("attack").or_insert(100); //没有就插入,返回值的引用
*stat += random_stat_buff();
player_stats.entry("mana").and_modify(|mana| *mana += 200).or_insert(100); // 有就加200,没有就插入100
println!("{:?}", player_stats);
9.自定义键类型
派生Eq和Hash。 我们还必须导出PartialEq。
例子
use std::collections::HashMap;
#[derive(Hash, Eq, PartialEq, Debug)]
struct Viking {
name: String,
country: String,
}
impl Viking {
/// 创建一个新的Viking。
fn new(name: &str, country: &str) -> Viking {
Viking { name: name.to_string(), country: country.to_string() }
}
}
// 使用HashMap存储Viking的健康点。
let vikings = HashMap::from([
(Viking::new("Einar", "Norway"), 25),
(Viking::new("Olaf", "Denmark"), 24),
(Viking::new("Harald", "Iceland"), 12),
]);
// 使用派生的实现来打印Viking的状态。
for (viking, health) in &vikings {
println!("{viking:?} has {health} hp");
}
10.其他
keys
返回一个迭代器,以任意顺序访问所有键。 迭代器元素类型为 &'a K。
例子
use std::collections::HashMap;
let map = HashMap::from([
("a", 1),
("b", 2),
("c", 3),
]);
for key in map.keys() {
println!("{key}");
}
values
返回一个以任意顺序访问所有值的迭代器。 迭代器元素类型为 &'a V。
例子
use std::collections::HashMap;
let map = HashMap::from([
("a", 1),
("b", 2),
("c", 3),
]);
for val in map.values() {
println!("{val}");
}
is_empty
如果map不包含任何元素,则返回true。
例子
use std::collections::HashMap;
let mut a = HashMap::new();
assert!(a.is_empty());
a.insert(1, "a");
assert!(!a.is_empty());
三、集合HashSet
HashSet是没有重复值的相同数据类型的值的集合。
Rust语言标准库std::collections中定义了结构体HashSet用于描述集合。
(一)定义集合
1.new()创建一个空集合
语法格式如下
let mut hash_set_name = HashSet::new();
2.可以从数组初始化HashSet:
use std::collections::HashSet;
let viking_names = HashSet::from(["Einar", "Olaf", "Harald"]);
(二)使用集合
1.获取集合的长度len()
len() 方法用于获取集合的长度,也就是集合中元素的个数。
范例
use std::collections::HashSet;
fn main() {
let mut languages = HashSet::new();
languages.insert("Python");
languages.insert("Rust");
languages.insert("Ruby");
languages.insert("PHP");
println!("size of the set is {}",languages.len());
}
编译运行结果如下
size of the set is 4
2.添加元素
insert() 用于插入一个值到集合中,如果集合中已经存在指定的值,则返回 false ,否则返回 true 。
例子
use std::collections::HashSet;
fn main() {
let mut languages = HashSet::new();
languages.insert("Python");
languages.insert("Rust");
languages.insert("Ruby");
languages.insert("PHP");
languages.insert("Rust"); // 插入失败但不会引发异常
println!("{:?}",languages);
}
编译运行结果如下
{"Python", "PHP", "Rust", "Ruby"}
3.删除集合元素
remove() 方法用于从集合中删除指定的值。
删除之前如果值 value 存在于集合中则返回 true ,如果不存在则返回 false 。
例子
use std::collections::HashSet;
fn main() {
let mut languages = HashSet::new();
languages.insert("Python");
languages.insert("Rust");
languages.insert("Ruby");
println!("length of the Hashset: {}",languages.len());
languages.remove(&"Kannan");
println!("length of the Hashset after remove() : {}",languages.len());
}
编译运行结果如下
length of the Hashset: 3
length of the Hashset after remove() : 3
clear
删除所有值。
例子
use std::collections::HashSet;
let mut v = HashSet::new();
v.insert(1);
v.clear();
assert!(v.is_empty());
4.获取元素
get() 方法用于获取集合中指定值的一个引用。
如果值 value 存在于集合中则返回集合中的相应值的一个引用,否则返回 None 。
例子
use std::collections::HashSet;
fn main() {
let mut languages = HashSet::new();
languages.insert("Python");
languages.insert("Rust");
languages.insert("Ruby");
languages.insert("PHP");
match languages.get(&"Rust"){
Some(value)=>{
println!("found {}",value);
}
None =>{
println!("not found");
}
}
println!("{:?}",languages);
}
编译运行结果如下
found Rust
{"Python", "Ruby", "PHP", "Rust"}
take
删除并返回集合中等于给定值的值 (如果有)。
该值可以是集合值类型的任何借用形式,但是借用形式上的Hash和Eq必须与值类型的那些匹配。
例子
use std::collections::HashSet;
let mut set = HashSet::from([1, 2, 3]);
assert_eq!(set.take(&2), Some(2));
assert_eq!(set.take(&2), None);
5.遍历集合
(1)使用引用
for book in &books {
println!("{book}");
}
(2)使用迭代器
iter()方法用于返回集合中所有元素组成的无序迭代器。
迭代器元素的顺序是无序的,毫无规则的。而且每次调用iter() 返回的元素顺序都可能不一样。
例子
use std::collections::HashSet;
fn main() {
let mut languages = HashSet::new();
languages.insert("Python");
languages.insert("Rust");
languages.insert("Ruby");
languages.insert("PHP");
for language in languages.iter() {
println!("{}",language);
}
}
编译运行结果如下
PHP
Python
Rust
Ruby
6.判断集合是否包含某个值
contains() 方法用于判断集合是否包含指定的值。
如果值 value 存在于集合中则返回 true ,否则返回 false 。
例子
use std::collections::HashSet;
fn main() {
let mut languages = HashSet::new();
languages.insert("Python");
languages.insert("Rust");
languages.insert("Ruby");
if languages.contains(&"Rust") {
println!("found language");
}
}
编译运行结果如下
found language
7.自定义类型
派生Eq和Hash。我们还必须导出PartialEq,这将在Eq中隐含在future中。
use std::collections::HashSet;
#[derive(Hash, Eq, PartialEq, Debug)]
struct Viking {
name: String,
power: usize,
}
let mut vikings = HashSet::new();
vikings.insert(Viking { name: "Einar".to_string(), power: 9 });
vikings.insert(Viking { name: "Einar".to_string(), power: 9 });
vikings.insert(Viking { name: "Olaf".to_string(), power: 4 });
vikings.insert(Viking { name: "Harald".to_string(), power: 8 });
for x in &vikings {
println!("{x:?}");
}
8.其他
is_empty
如果集合不包含任何元素,则返回true。
例子
use std::collections::HashSet;
let mut v = HashSet::new();
assert!(v.is_empty());
v.insert(1);
assert!(!v.is_empty());
union
访问表示并集的值,即self或other中的所有值,没有重复项。
例子
use std::collections::HashSet;
let a = HashSet::from([1, 2, 3]);
let b = HashSet::from([4, 2, 3, 4]);
// 以任意顺序打印1、2、3、4。
for x in a.union(&b) {
println!("{x}");
}
let union: HashSet<_> = a.union(&b).collect();
assert_eq!(union, [1, 2, 3, 4].iter().collect());
intersection
访问表示相交的值,即self和other中的值。
当self和other中存在相等的元素时,生成的Intersection可能会引用其中一个。 如果T包含未通过其Eq实现进行比较的字段,并且可能在两组T的两个相等副本之间保持不同的值,则这可能是相关的。
例子
use std::collections::HashSet;
let a = HashSet::from([1, 2, 3]);
let b = HashSet::from([4, 2, 3, 4]);
// 以任意顺序打印2,3。
for x in a.intersection(&b) {
println!("{x}");
}
let intersection: HashSet<_> = a.intersection(&b).collect();
assert_eq!(intersection, [2, 3].iter().collect());
is_subset
如果集合是另一个集合的子集,则返回true,即other至少包含self中的所有值。
例子
use std::collections::HashSet;
let sup = HashSet::from([1, 2, 3]);
let mut set = HashSet::new();
assert_eq!(set.is_subset(&sup), true);
set.insert(2);
assert_eq!(set.is_subset(&sup), true);
set.insert(4);
assert_eq!(set.is_subset(&sup), false);
is_superset
如果集合是另一个集合的超集,则返回true,即self至少包含other中的所有值。
例子
use std::collections::HashSet;
let sub = HashSet::from([1, 2]);
let mut set = HashSet::new();
assert_eq!(set.is_superset(&sub), false);
set.insert(0);
set.insert(1);
assert_eq!(set.is_superset(&sub), false);
set.insert(2);
assert_eq!(set.is_superset(&sub), true);