【百例RUST - 009】容器 Vector

【百例RUST - 009】容器 Vector

第一章 基础用法

第01节 快速定义和使用

案例代码

rust 复制代码
fn main(){
    // 创建空的 Vector 因为他是一个容器, 如果是不可变的没有意义, 需要存放内容, 所以一般定义为可变类型
    let mut one : Vec<i32> = Vec::new();
    
    // 向容器里面存放几个数据
    one.push(11);
    one.push(22);
    one.push(33);
    one.push(44);

    // 需要获取容器的内容, 输出
    println!("one 的 0号索引位置 = {}", &one[0]);
    println!("one 的 1号索引位置 = {}", &one[1]);
    println!("one 的 2号索引位置 = {}", &one[2]);
    println!("one 的 3号索引位置 = {}", &one[3]);
}

// one 的 0号索引位置 = 11
// one 的 1号索引位置 = 22
// one 的 2号索引位置 = 33
// one 的 3号索引位置 = 44

通过上述案例,我们可以知道,

可变空的Vector的定义格式如下

复制代码
let mut 变量名称: Vec<数据类型> = Vec::new();

存放数据采用的是

复制代码
push(元素) 

获取指定所有位置的元素方式1

复制代码
&变量名称[索引值]

第02节 直接带有值的定义

前面介绍的是 可变的、空的 容器定义方式

下面介绍的是 带有值的定义方式

rust 复制代码
fn main(){
    // 创建有值的容器
    let mut one : Vec<i32> = vec![1,2,3];
    
    // 向容器里面存放几个数据
    one.push(11);
    one.push(22);
    one.push(33);
    one.push(44);

    // 需要获取容器的内容, 输出
    println!("one 的 0号索引位置 = {}", &one[0]);
    println!("one 的 1号索引位置 = {}", &one[1]);
    println!("one 的 2号索引位置 = {}", &one[2]);
    println!("one 的 3号索引位置 = {}", &one[3]);
}


// one 的 0号索引位置 = 1
// one 的 1号索引位置 = 2
// one 的 2号索引位置 = 3
// one 的 3号索引位置 = 11

第03节 推荐获取值的方式

rust 复制代码
fn main(){
    // 创建空的 Vector 因为他是一个容器
    let mut one : Vec<i32> = Vec::new();
    
    // 向容器里面存放几个数据
    one.push(11);
    one.push(22);
    one.push(33);
    one.push(44);

    // 通过索引, 获取指定位置的数据, 该数据类型是一个 Option<泛型> 类型的数据
    let opt = one.get(0);
    // 针对于 Option 进行 match 匹配
    match opt {
        None => println!("nothing"),
        Some(i) => println!("result = {}", i),
    }
}

// result = 11

推荐这种方式获取值,是因为存在容错机制处理,如果访问了超过索引范围的数据值,不会出现崩溃!

第04节 遍历容器

不可变的遍历方式。 变量过程中,值没有改变

rust 复制代码
fn main(){
    // 创建空的 Vector 因为他是一个容器
    let mut one : Vec<i32> = Vec::new();
    
    // 向容器里面存放几个数据
    one.push(11);
    one.push(22);
    one.push(33);
    one.push(44);

    // 遍历容器的数据
    for i in &one{
        println!("内容: {}", i);
    }
}

// 内容: 11
// 内容: 22
// 内容: 33
// 内容: 44

可变的遍历方式。遍历过程中,值可以改变。

rust 复制代码
fn main(){
    // 创建空的 Vector 因为他是一个容器
    let mut one : Vec<i32> = Vec::new();
    
    // 向容器里面存放几个数据
    one.push(11);
    one.push(22);
    one.push(33);
    one.push(44);

    // 遍历容器的数据
    for i in &mut one{
        *i = *i+1;
        println!("内容: {}", i);
    }
}

// 内容: 12
// 内容: 23
// 内容: 34
// 内容: 45

知识点: 这里的 *i 是什么意思?

理解这个问题,需要解答下面的几个问题?

1、在循环当中使用的 i 是什么?

2、为什么必须加上星号(解引用)?

3、不加星号,会怎样?

4、深入理解,自动解引用的场景?

问题一

复制代码
问题:  在循环当中使用的  i  是什么?
	
回答:
	在我们的代码当中    for  i  in  &mut  one
	这种写法, 会让 i 变成 可变引用( &mut i32) 我们可以将 i 想象成为一张 "指向内存地址的小纸条"

问题二

复制代码
问题:  为什么必须加上星号(解引用)?
	
回答:
	在 rust 代码当中,  * 是 【解引用】的操作符。
		i  的身份: 他是 (&mut i32) 也就是 指向某个整数的指针引用。他存放的是地址。
		*i 的身份: 他是 i32 也就是那个地址里面, 存放的具体整数值。
	*i = *i + 1  的逻辑过程:
    	第一步: 取值, 右边的 *i 去地址里面把那个整数(比如 11)取出来
    	第二步: 运算, 执行 11+1 得到 12
    	第三步: 赋值, 左边的 *i 把计算得到的结果 12 写回到那个地址所指向的内存空间里面。

问题三

复制代码
问题: 不加星号会怎样呢?

回答:
	如果我们尝试写 i=i+1 编辑器会对我们 发出歇斯底里的"咆哮" SB 原因有两点:
	1、类型不匹配:  
		i 是一个 "引用/地址"  而 1 是一个整数。 我们不能把一个地址和一个整数直接相加。
	2、所有权与赋值限制:
		在循环当中, i 作为一个迭代的变量, 如果我们试图改变 i 本身, 也就是让这张纸条指向另外的一个地址, 
		在 Rust 的for循环语法当中, 通常情况下是不被允许的。

问题四

复制代码
问题: 深入理解,自动解引用的场景

回答:
	我们可能会存在疑问, 为什么下面写的内容  println!("内容: {}", i);  没有添加星号, 也可以打印出数字呢?
	这里原因是因为, RUST 很贴心, println! 宏会自动处理引用, 他会自动寻找引用背后的值, 并且打印出来。
	所以:
		println!("{}", i);    // 自动解引用, 方便阅读
		println!("{}", *i);   // 手动解引用, 效果一样
	因此:
		如果我们在做数学运算(如+) 或者重新赋值的时候, RUST 要求我们必须显示使用 *星号 来明确我们的意图: 我们是想要操作那个值。

第05节 在容器中使用枚举

案例代码

rust 复制代码
#[derive(Debug)]
enum Context {
    Text(String),
    Float(f32),
    Int(i32),
}

fn main(){
    // 使用枚举存放到容器中
    let mut one : Vec<Context> = Vec::new();
    
    // 向容器里面存放几个数据
    one.push(Context::Text(String::from("hellowrold")));
    one.push(Context::Int(32));
    one.push(Context::Float(3.66));

    // 遍历容器的数据
    for con in & one{
        println!("内容: {:?}", con);
    }
}

//  内容: Text("hellowrold")
//  内容: Int(32)
//  内容: Float(3.66)

第二章 常用函数

第01节 添加函数

直接添加

rust 复制代码
fn main(){
    // 定义容器
    let mut one : Vec<i32> = Vec::new();
    
    // 向容器里面存放几个数据
    one.push(11);
    one.push(22);
    one.push(33);

    // 遍历容器的数据
    for i in & one{
        println!("内容: {}", i);
    }
}

// 内容: 11
// 内容: 22
// 内容: 33

插入元素

rust 复制代码
fn main(){
    // 定义容器
    let mut one : Vec<i32> = Vec::new();
    
    // 向容器里面存放几个数据
    one.push(11);
    one.push(22);
    one.push(33);
    one.push(44);

    // 1、向指定的索引位置添加元素, 之前的位置靠后移动。
    // 2、这里的第一个参数, 与前面容器的容量有关, 
    //      前面的容器容量大小是4, 这里第一个参数的取值是 0,1,2,3,4
    //      如果这里的第一个参数, 超过了4, 例如(5,6,7,8...)则会出现崩溃!
    one.insert(2, 66);

    // 遍历容器的数据
    for i in & one{
        println!("内容: {}", i);
    }
}

// 内容: 11
// 内容: 22
// 内容: 66
// 内容: 33
// 内容: 44

第02节 删除元素

移除最后的一个元素

rust 复制代码
fn main(){
    // 定义容器
    let mut one : Vec<i32> = Vec::new();
    
    // 向容器里面存放几个数据
    one.push(11);
    one.push(22);
    one.push(33);
    one.push(44);

    // 移除最后的一个元素
    one.pop();

    // 遍历容器的数据
    for i in & one{
        println!("内容: {}", i);
    }
}

// 内容: 11
// 内容: 22
// 内容: 33

移除指定索引位置的元素

rust 复制代码
fn main(){
    // 定义容器
    let mut one : Vec<i32> = Vec::new();
    
    // 向容器里面存放几个数据
    one.push(11);
    one.push(22);
    one.push(33);
    one.push(44);

    // 移除指定索引位置的元素
    // 这里的参数, 与前面容器的容量有关, 表示的是索引位置, 从0开始到最大容量-1
    // 例如: 当前容器存放了4个元素, 那么参数的取值只能是 (0,1,2,3)
    let num = one.remove(2);

    println!("被移除的元素是: {}", num);

    // 遍历容器的数据
    for i in & one{
        println!("内容: {}", i);
    }
}

// 被移除的元素是: 33
// 内容: 11
// 内容: 22
// 内容: 44

第03节 修改元素

修改指定索引位置的元素值

rust 复制代码
fn main(){
    // 定义容器
    let mut one : Vec<i32> = Vec::new();
    
    // 向容器里面存放几个数据
    one.push(11);
    one.push(22);
    one.push(33);
    one.push(44);

    // 修改指定索引位置的元素值
    one[2] = 666; 

    // 遍历容器的数据
    for i in & one{
        println!("内容: {}", i);
    }
}

// 被移除的元素是: 33
// 内容: 11
// 内容: 22
// 内容: 666
// 内容: 44

更加安全的做法

rust 复制代码
fn main(){
    // 定义容器
    let mut one : Vec<i32> = Vec::new();
    
    // 向容器里面存放几个数据
    one.push(11);
    one.push(22);
    one.push(33);
    one.push(44);

    // 查询当前容器的容量大小
    let number: Option<&mut i32> = one.get_mut(2);

    match number {
        None => print!("数据不存在"),
        Some(x) => *x = 666,
    }

    // 遍历容器的数据
    for i in & one{
        println!("内容: {}", i);
    }
}

// 内容: 11
// 内容: 22
// 内容: 666
// 内容: 44

第04节 查询内容

查询容量大小

rust 复制代码
fn main(){
    // 定义容器
    let mut one : Vec<i32> = Vec::new();
    
    // 向容器里面存放几个数据
    one.push(11);
    one.push(22);
    one.push(33);
    one.push(44);

    // 查询当前容器的容量大小
    let len: usize = one.len();
    println!("result = {}", len);
    
}

// result = 4

查询当前容器是否是空数据

rust 复制代码
fn main(){
    // 定义容器
    let mut one : Vec<i32> = Vec::new();
    
    // 向容器里面存放几个数据
    one.push(11);
    one.push(22);
    one.push(33);
    one.push(44);

    // 查询当前容器是否是空数据
    let empty: bool = one.is_empty();
    println!("result = {}", empty);
}

// result = false

第三章 几个问题

第01节 查找指定元素

查询容器中,是否包含有指定的元素,如果存在则返回当前元素第一次出现的索引位置,如果不存在,则返回-1

案例代码如下:

rust 复制代码
fn main(){
    // 定义容器
    let mut one : Vec<i32> = Vec::new();
    
    // 向容器里面存放几个数据
    one.push(11);
    one.push(22);
    one.push(33);
    one.push(44);
    one.push(33);
    one.push(66);

    // 查询容器当中, 是否存在指定的元素, 如果存在则返回当前元素第一次出现的索引位置, 否则返回-1
    let target: i32 = 33;

    // 实现查询的逻辑, 采用 iter() 获取迭代器
    let result = one.iter().position(|&x|x == target);
    // 将结果转换为 i32 类型, 如果没有找到则返回 -1
    let index: i32 = match result {
        Some(i) => i as i32,
        None => -1,
    };
    // 输出结果
    println!("元素{} 的索引位置是{}", target, index);
}

// 元素33 的索引位置是2

详细说明一下上面的代码

复制代码
1、寻找位置  iter().position(....)
	let result = one.iter().position(|&x|x == target);
	上述代码, 主要做了三件事情:
	第一件事情: 
		iter()
		它把 Vec<i32> 变成了一个迭代器, 迭代器就像传送带一样, 把容器当中的元素, 一个一个的交给后面的函数处理。
	第二件事情:
		pisition(...)
		这里迭代器当中的内置方法。 它会从左到右检查每一个元素,直到找到符合条件的元素为止。
		需要注意的是, 他返回的结果是 Option<usize> 类型
	第三件事情:
		|&x| x == target
		这是一个匿名的函数, 做了一个闭包的操作。 
		&x 因为 .iter() 提供的是元素的引用, 所以我们使用 &x 来解构。直接拿到数据值 x
		x == target 这是一个逻辑判断, 如果相等, position 就会停止搜索。
		

2、处理结果  match 模式匹配
	因为 position 返回的是 Option 类型, (要么是"有", 要么是"无")
	RUST 强制要求我们必须处理这两种可能性。

第02节 所有权的问题

问题代码

rust 复制代码
fn main(){
    // 定义容器
    let mut one : Vec<i32> = Vec::new();
    
    // 向容器里面存放几个数据
    one.push(11);
    one.push(22);
    one.push(33);
    one.push(44);

    // 获取第一个元素的引用
    let first: &i32 = &one[0];
    one.push(55);
    // 目前这里就会出现问题了
    println!("first = {}", first);  
}

分析原因

复制代码
一句话解释问题:
	我们不能在手里拿着某个东西的 "票据"(引用)的时候, 又让人去改变这个东西本身(修改容器)
	
详细说明一下:
	我们理解一下 Vec 在内存当中的工作流程
	1、内存分配
		当我们创建了 Vec 并且向里面 push 元素的时候, RUST 会在堆内存当中申请一块空间。
	2、获取引用
		执行了 let first = &one[0]; 的时候, first 指向了这块内存的首地址。
	3、重新分配(关键点)
		当我们执行  one.push(55); 的时候, 如果原本申请的内存空间就不够用了, 那么 Vec 会执行下面的操作:
		A. 申请一块更大的 新的内存
		B. 将就得数据拷贝到新的内存中
		C. 释放旧内存
	4、悬垂指针(问题出现)
		如果允许 push 成功,那么 first 仍然会指向那块已经被释放的旧内存。
		这个时候, 如果我们继续打印 first 程序就会访问到非法的地址,导致崩溃。
		这在 C++当中是常见的 Bug 但是在 RUST 当中, 被编译器禁止了。

如何修改这个问题呢?

我们只是获取第一个元素的值,不去获取 容器内部的引用,直接把值拷贝出来,就可以了。

修改方案: 去掉了 & 引用符

rust 复制代码
fn main(){
    // 定义容器
    let mut one : Vec<i32> = Vec::new();
    
    // 向容器里面存放几个数据
    one.push(11);
    one.push(22);
    one.push(33);
    one.push(44);

    // 获取第一个元素的值
    let first: i32 = one[0];
    one.push(55);
    // 目前这里就会出现问题了
    println!("first = {}", first);  
}

//  first = 11
相关推荐
环流_2 小时前
多线程3(线程安全问题及解决方案)
java·开发语言
覆东流2 小时前
第2天:Python变量与数据类型
开发语言·后端·python
Gofarlic_oms12 小时前
制定企业Citrix虚拟化软件资产管理政策框架
运维·服务器·开发语言·matlab·负载均衡
添砖java。。。3 小时前
java实现mqtt链接并控制门锁设备
java·开发语言
codeejun3 小时前
每日一Go-53、Go微服务--限流与降级
开发语言·微服务·golang
阿里嘎多学长3 小时前
2026-04-17 GitHub 热点项目精选
开发语言·程序员·github·代码托管
Wadli3 小时前
集群C++聊天服务器
服务器·开发语言·c++
凭君语未可3 小时前
为什么需要代理?从一个基础问题理解 JDK 静态代理
java·开发语言
luoqice3 小时前
利用flv库读取flv文件时长c程序
c语言·开发语言