Rust第八节 - 通用集合类型

8 通用集合类型

通用集合类型分为三种:动态数组,动态字符串,哈希映射,他们都会被存储在内存堆上。我们首先来看看动态数组。

8.1 动态数组

对于动态数组而言,我们只能存储相同数据类型的值。 首先我们先来创建一个动态数组,我们可以使用Vec::new(),例如:

rust 复制代码
let arr: Vec<i8> = Vec::new();

如果我们想往里面存储值,我们可以调用该实例的push方法,但是我们得先将变量名声明为可变mut,例如:

rust 复制代码
let mut arr: Vec<i8> = Vec::new();
arr.push(1);
arr.push(2);
println!("{:?}", arr)

我们使用Vec::new()只能声明一个空的动态数组,如果我们想直接生成一个具有初始值的动态数组呢? 我们可以使用vec!宏,例如:

rust 复制代码
let arr: Vec<i8> = vec![1,2,3];

我们在最开始提到,动态数组只能存储相同的数据类型,但是如果我们存储的值有字符串,数字等等呢?这个时候我们可以结合着枚举来使用,存储不同数据类型的值,例如:

rust 复制代码
#[derive(Debug)]
enum StoreData {
    Int(i32),
    Float(f64),
    Str(String),
    Boolean(bool),
}

let mut arr: Vec<StoreData> = Vec::new();

arr.push(StoreData::Float(1.0)); // 存储浮点数
arr.push(StoreData::Int(2)); // 存储整数
arr.push(StoreData::Str(String::from("这是一串字符"))); // 存储字符串
arr.push(StoreData::Boolean(true)); // 存储布尔值
println!("{:?}", arr);

讲了创建动态数组,接下来我们讲讲如何获取动态数组的值。 一般有以下两种方式,一种是使用索引,另一种是使用get方法。我们先使用索引的方式,例如:

rust 复制代码
let arr = vec![1, 2, 3];
println!("{}", &arr[0]); // 正常输出1

但是如果我们访问一个,超过数组长度的索引,会报越界访问的错误,例如:

rust 复制代码
println!("{}", &arr[5]); // index out of bounds: the len is 3 but the index is 5
// 所以我们需要要避免越界访问的出现

但是如果我们使用get的形式,去获取动态数组的值,它会返回一个Option的枚举,如果出现访问越界 的情况,我们可以使用Option::None去处理,例如:

rust 复制代码
let arr = vec![1, 2, 3];
match &arr.get(5) {
    Option::Some(i) => {
        println!("{}", i);
    }
    Option::None => {
        println!("出现数组越界的情况"); // match匹配会达到这里,因为出现了数组越界的情况
    }
}

当然,如果我们需要批量读取数据的时候,这个时候我们就需要使用循环了,例如:

rust 复制代码
let arr = vec![1, 2, 3];
for i in &arr {
    println!("{}", i);
}

那如果我们要修改里面的数据,比如,我需要将数组里面的数据都乘以2, 例如:

rust 复制代码
let mut arr = vec![1, 2, 3];
for i in &mut arr {
    *i *= 2; // 这里我们需要对元素解引用才能修改元素
    println!("{}", i);
}

8.2 动态字符串

接下来我们讲讲,如何创建一个动态字符串,它和动态数组有点类似,我们可以直接使用String上面的new方法,例如:

rust 复制代码
let str: String = String::new();

这个实例创建好了之后,是没有初始值的,我们可以使用push或者pus_str方法手动为他赋值,例如:

rust 复制代码
let mut str = String::new();
str.push('h'); // push 方法只能传一个字符进去
str.push('e');
str.push('l');
str.push('l');
str.push('o');
str.push_str(", world"); // push_str可以传入一个字符串字面量
println!("{}", str);

当然我们可以直接将字符串相加来得到一个最终的字符串,例如:

rust 复制代码
let mut str1 = String::new();
str1.push_str("hello");
let mut str2 = String::new();
str2.push_str("world");
let str3 = str1 + &str2; // 实质是调用了内置的一个add(self, &str) -> String
println!("{}", str3);
println!("{}", str1); // 报错,这里str1的所有权转移到了str3;

但是这样显得非常得复杂,所以我们使用format!宏来快速拼接字符串,例如:

rust 复制代码
let mut str1 = String::new();
str1.push_str("hello");
let mut str2 = String::new();
str2.push_str("world");
let str3 = format!("{}{}", str1, str2);
println!("{}", str3);

当我们我们也可以使用String::from快速地创建并且初始化值,例如:

rust 复制代码
let str: String = String::from("hello, world");

如果我们有一个字符串字面量,我们想把它转成String类型, 我们可以使用to_string去转化,例如:

rust 复制代码
let new_str: String = "hello, world".to_string();
println!("{:?}", new_str);

字符串是不支持使用索引获取的,那我们应该如何获取字符串的某一段的值呢,这个时候我们可以使用之前提到过的字符串切片,例如:

rust 复制代码
let str_value = String::from("hello, world");
let new_str = &str_value[0..1];
println!("{}", new_str);

但是这里有个问题,因为rust是使用UTF-8作为编码格式,每一个字符占一个字节,但是如果是Unicode标量的话,它会占用两个字节,如果我们使用字符串切片不当就会报错,例如:

rust 复制代码
let str_value = String::from("дравствуйте");
let str_slice = &str_value[0..1]; // 因为д占两个字节,这里我只取一个字节,就会发生panic,所以对字符串使用切片需要小心谨慎
println!("{}", str_slice)

我们还可以遍历每个标量值,例如:

rust 复制代码
let str = String::from("Здравствуйте");
for i in str.chars() {
    println!("{}", i);
}

也可以把每个字节都遍历出来,例如:

rust 复制代码
let str = String::from("Здравствуйте");
for i in str.bytes() {
    println!("{}", i);
}

8.3 哈希映射

在rust中,我们创建HashMap同样使用其中的new方法,因为该集合类型用的比较少,所以不在rust的预导入模块中,我们需要使用use std::collections::HashMap;手动引入,例如:

rust 复制代码
use std::collections::HashMap;
let hm = HashMap::new();

如果我们想往里面添加值的话,我们使用insert方法,例如:

rust 复制代码
use std::collections::HashMap;
let mut hm = HashMap::new();
hm.insert(String::from("stephen"), 20);
hm.insert(String::from("james"), 30);
println!("{:?}", hm);

我们也可以使用collect方法把动态数组转化为哈希映射,其中zip的作用是创建一个元祖的数组,例如:

rust 复制代码
use std::collections::HashMap;

let arr1 = vec![String::from("stephen"), String::from("james")];
let arr2 = vec![20, 30];
let ages: HashMap<_, _> = arr1.iter().zip(arr2.iter()).collect();
println!("{:?}", ages)

我们同样也可以使用get方法,去获哈希映射里面的值,它返回的也是Option枚举,例如:

rust 复制代码
hm.get(&String::from("stephen")) // Some(20),这里我们需要使用值的引用

遍历哈希映射也是使用for, 例如:

rust 复制代码
for (key, value) in &hm {
    println!("{}-{}", key, value);
}
// james-30
// stephen-20

那我们应该怎么去覆盖之前的值呢?首先我们得先判断哈希映射里面有没有当前这个字段,这时候我们需要使用entry来判断,然后如果不存在的话,可以使用or_insert来插入值,有得话就会直接更新,例如:

rust 复制代码
use std::collections::HashMap;
let mut hm = HashMap::new();
hm.insert(String::from("stephen"), 20);
hm.insert(String::from("james"), 30);
hm.entry(String::from("kyrie")).or_insert(20);
println!("{:?}", hm)
相关推荐
顾安r3 小时前
11.8 脚本网页 星际逃生
c语言·前端·javascript·flask
Hello.Reader3 小时前
Data Sink定义、参数与可落地示例
java·前端·网络
im_AMBER3 小时前
React 17
前端·javascript·笔记·学习·react.js·前端框架
谷歌开发者4 小时前
Web 开发指向标 | Chrome 开发者工具学习资源 (六)
前端·chrome·学习
一晌小贪欢4 小时前
【Html模板】电商运营可视化大屏模板 Excel存储 + 一键导出(已上线-可预览)
前端·数据分析·html·excel·数据看板·电商大屏·大屏看板
发现你走远了4 小时前
连接模拟器网页进行h5的调试(使用Chrome远程调试(推荐)) 保姆级图文
前端·chrome
街尾杂货店&5 小时前
css - 实现三角形 div 容器,用css画一个三角形(提供示例源码)简单粗暴几行代码搞定!
前端·css
顺凡5 小时前
删一个却少俩:Antd Tag 多节点同时消失的原因
前端·javascript·面试
小白路过5 小时前
CSS transform矩阵变换全面解析
前端·css·矩阵
爬山算法6 小时前
Redis(110)Redis的发布订阅机制如何使用?
前端·redis·bootstrap