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)
相关推荐
人工智能训练师3 小时前
Ubuntu22.04如何安装新版本的Node.js和npm
linux·运维·前端·人工智能·ubuntu·npm·node.js
Seveny073 小时前
pnpm相对于npm,yarn的优势
前端·npm·node.js
yddddddy4 小时前
css的基本知识
前端·css
昔人'4 小时前
css `lh`单位
前端·css
Nan_Shu_6145 小时前
Web前端面试题(2)
前端
知识分享小能手6 小时前
React学习教程,从入门到精通,React 组件核心语法知识点详解(类组件体系)(19)
前端·javascript·vue.js·学习·react.js·react·anti-design-vue
蚂蚁RichLab前端团队6 小时前
🚀🚀🚀 RichLab - 花呗前端团队招贤纳士 - 【转岗/内推/社招】
前端·javascript·人工智能
孩子 你要相信光7 小时前
css之一个元素可以同时应用多个动画效果
前端·css
huangql5207 小时前
npm 发布流程——从创建组件到发布到 npm 仓库
前端·npm·node.js
Days20507 小时前
LeaferJS好用的 Canvas 引擎
前端·开源