【Rust自学】8.1. Vector

news2025/1/3 8:56:08

喜欢的话别忘了点赞、收藏加关注哦(加关注即可阅读全文),对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=・ω・=)

8.1.0. 本章内容

第八章主要讲的是Rust中常见的集合。Rust中提供了很多集合类型的数据结构,这些集合可以包含很多值。但是第八章所讲的集合与数组和元组有所不同。

第八章中的集合是存储在堆内存上而非栈内存上的,这也意味着这些集合的数据大小无需在编译时就确定,在运行时它们可以动态地变大或变小。

本章主要会讲三种集合:Vector(本文)、String和HashMap

喜欢的话别忘了点赞、收藏加关注哦(加关注即可阅读全文),对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=・ω・=)

8.1.1. 使用Vector存储多个值

Vector这个类型的写法是Vec<T>,同样的,T代表泛型变量,在实际使用时替换成自己需要的数据类型即可。

Vector由标准库提供,在Vector里可以存储多个值,而且这些值的类型是相同的,它们在内存中是连续存放的。可以把它视为可以扩展的数组。

创建Vector可以使用Vec::new这个函数,我们看个例子:

fn main() {  
    let v:Vec<i32> = Vec::new();  
}

这个例子很简单,就是使用Vec::new这个函数声明了一个Vector里边元素是i32的变量。

在这里需要填在Vec<>里填i32是因为Vec::new它创建的是一个空的Vector,里面没有元素,又因为没有前后文供Rust推断,所以Rust就推断不出来这个变量里的元素是什么类型的,就会报错。所以这里需要显式声明。如果有前后文供Rust推断,Rust就能够自行判断Vector里的元素类型。

而在一般情况下,会使用指定初始值的方式来创建Vector,这样的方式就可以使用Veec!这个宏。如下例:

fn main() {  
    let v = vec![1, 2, 3];  
}

这里就不需要显式声明Vector里的元素类型了,因为Rust编译器根据初始值1、2、3推断出了元素类型是i32

8.1.2. 如何更新Vector

1. 添加元素

向Vector里添加元素使用push这个方法。如下例:

fn main() {  
    let mut v = Vec::new();  
    v.push(1);  
}
  • 注意,向Vector里添加元素的前提是这个Vector是可变变量,所以在声明的时候需要mut关键字。
  • 这里的let mut v = Vec::new();也没有显式声明元素类型,但是Rust编译器通过下文向Vector里添加1的操作推断出了元素类型是i32

2. 删除Vector

与任何其他的struct结构体一样,当Vector离开作用域后,它和它里面的元素就会被清理掉。

3. 读取Vector的元素

一共有两种方式可以应用Vector里面的值,一种是使用索引,一种是使用get方法。如下例:一个Vector,里面存有1 2 3 4 5,访问并打印出第三个元素。

fn main() {  
    let v = vec![1, 2, 3, 4, 5];  
    let third = &v[2];//索引  
    println!("The third element is {}", third);  
  
    match v.get(2) {  //get方法加match
        Some(third) => println!("The third element is {}", third),  
        None => println!("There is no third element."),  
    };  
}
  • let third = &v[2];是使用索引的方式,访问第三个元素就是索引2的位置,所以[]内写2。而在变量v前加上&表示是引用。
  • v.get(2)就是使用get方法来实现读取的,但由于get的返回值是Option这个枚举类型(在6.2. Option枚举中讲过,这里不再赘述),所以要使用match表达式(在6.3. 控制流运算符-match中讲过match)来解包。
    如果能从这个索引取到值,那么就会把这个索引下的值绑定给third这个变量,然后在后面的代码块中输出。如果不能,返回的是None这个变体,就会打印"There is no third element."。

这两者的实现方法比较不同,效果是一样的。但如果是非法的访问(比如访问的索引越界了,超过了实际Vector的长度),两种将会有一些区别。

先试试使用索引:

fn main() {
	let v = vec![1, 2, 3, 4, 5];
	let third = &v[100]; //索引100越界了
	println!("The third element is {}", third);
}

输出:

index out of bounds: the len is 5 but the index is 100

程序触发了panic!,终止了程序执行,警告了索引越界。

再试试使用get:

fn main() {
	let v = vec![1, 2, 3, 4, 5];
	match v.get(100) {  //索引100越界了
        Some(third) => println!("The third element is {}", third),  
        None => println!("There is no third element."),  
    };  
}

输出:

There is no third element.

因为get函数不能从索引100上获取东西,所以它就会返回None

在写代码时,就需要确定自己的需求。遇到越界的情况,想要直接触发panic!结束程序就用所以找元素,其余的情况用get函数最好。

8.1.3. 所有权和借用规则

还记得在4.2. 所有权规则、内存与分配中讲的借用规则吗?同一个作用域内不能同时有可变和不可变引用。这个规则在Vector依然是适用的。看个例子:

fn main() {
	let mut v = vec![1, 2, 3, 4, 5];
	let first = &v[0];
	v.push(6);
	println!("The first element is {}", first);
}

输出:

error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
 --> src/main.rs:4:5
  |
3 |     let first = &v[0];
  |                  - immutable borrow occurs here
4 |     v.push(6);
  |     ^^^^^^^^^ mutable borrow occurs here
5 |     println!("The first element is {}", first);
  |                                         ----- immutable borrow later used here
  • push函数的参数是&mut self, value: T&mut表示push函数会把传进来的变量作为可变引用来处理。在例子中就是v在这里有一个可变的引用。
  • let first = &v[0];这里的firstv的不可变引用,两者又在同一个作用域下,所以会报错
  • println!会把传进去的变量作为不可变引用。

在这个作用域内,同时出现了可变和不可变引用,所以程序会报错。

但有人可能会疑惑——push函数是往Vector的后面加东西,而前面的元素不会受影响,为什么Rust要搞这么麻烦的设计?

这是因为在内存中Vector的元素是连续存储的,如果往后面加一个元素,正好又有东西占用了后面的内存,腾不出地方放新的元素,系统就得重新分配内存,找个足够大的地方来放置添加了元素之后的Vector。这样的话,原来的那块内存就会被释放或者重新分配掉,但引用仍然会指向原先的那内存地址,造成悬空引用(在4.4. 引用与借用中有讲)

8.1.3. 遍历Vector里的值

使用for循环是最常见的方法。如下例:

fn main() {  
    let v = vec![1, 2, 3, 4, 5];  
    for i in &v {  
        println!("{}", i);  
    }  
}

输出效果:

1
2
3
4
5

当然,如果想要在循环里修改元素也是可以的,只需要把v声明成可变的,把&v改成&mut v即可:

fn main() {  
    let mut v = vec![1, 2, 3, 4, 5];  
    for i in &mut v {  
        *i += 10;  
    }  
    for i in v {  
        println!("{}", i);  
    }  
}

注意:第四行的*i前面之所以有个*是因为i在本质上是&mut i32类型,存储的是指针而不是实际的i32值,需要先解引用,使i变为i32类型获得实际的值才能进行加减操作。

输出效果:

11
12
13
14
15

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2268469.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

挖空的解决思路

用RagFlow解析完文档后怎么对某些实体的某些参数进行遮挡&#xff0c;给他设置预设好的可选项&#xff0c;并最终整合成文档模版&#xff0c;给我详细讲解怎么实现 解析–实体抽取&#xff08;Open NRE、UIE&#xff09;–遮挡–插入可选项–保存模版1–微调训练得模版2

【AI日记】24.12.30 kaggle 比赛 2-18

【AI论文解读】【AI知识点】【AI小项目】【AI战略思考】【AI日记】 工作 参加&#xff1a;kaggle 比赛 Regression with an Insurance Dataset时间&#xff1a;8 小时 读书 1 书名&#xff1a;教育的本质时间&#xff1a;0.5 小时评估&#xff1a;快速读完&#xff0c;收获不…

javaweb 04 springmvc

0.1 在上一次的课程中&#xff0c;我们开发了springbootweb的入门程序。 基于SpringBoot的方式开发一个web应用&#xff0c;浏览器发起请求 /hello 后 &#xff0c;给浏览器返回字符串 “Hello World ~”。 其实呢&#xff0c;是我们在浏览器发起请求&#xff0c;请求了我们…

【C++】九九乘法表编程题详解与多角度对比分析

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;题目概述题目描述 &#x1f4af;老师的实现方法代码解析优点不足 &#x1f4af;我的实现方法代码解析优点不足 &#x1f4af;实现方法对比&#x1f4af;优化与扩展代码优化…

保险公司开辟新模式:智能ai搭建咨询帮助中心

随着保险行业的快速发展&#xff0c;消费者对保险服务的期望也在不断提高。从传统的电话咨询到在线客服&#xff0c;服务模式的不断升级旨在提供更加便捷、高效的客户服务。然而&#xff0c;面对日益复杂的保险产品和多样化的客户需求&#xff0c;传统的人工客服体系逐渐显露出…

雷电模拟器安装LSPosed

雷电模拟器最新版支持LSPosed。记录一下安装过程 首先到官网下载并安装最新版&#xff0c;我安装的时候最新版是9.1.34.0&#xff0c;64位 然后开启root和系统文件读写 然后下载magisk-delta-6并安装 ,这个是吾爱破解论坛提供的&#xff0c;号称适配安卓7以上所有机型&#x…

使用uWSGI将Flask应用部署到生产环境

使用uWSGI将Flask应用部署到生产环境&#xff1a; 1、安装uWSGI conda install -c conda-forge uwsgi&#xff08;pip install uwsgi会报错&#xff09; 2、配置uWSGI 在python程序的同一文件夹下创建 uwsgi.ini文件&#xff0c;文件内容如下表。 需要按照实际情况修改文件名称…

计算机网络 (15)宽带接入技术

前言 计算机网络宽带接入技术是指通过高速、大容量的通信信道或网络&#xff0c;实现用户与互联网或其他通信网络之间的高速连接。 一、宽带接入技术的定义与特点 定义&#xff1a;宽带接入技术是指能够传输大量数据的通信信道或网络&#xff0c;其传输速度通常较高&#xff0c…

资源规划管理系统(源码+文档+部署+讲解)

引言 在当今快速发展的商业环境中&#xff0c;企业资源规划&#xff08;ERP&#xff09;系统已成为企业运营的核心。本文将深入探讨一套全新的ERP系统源代码&#xff0c;该系统基于先进的技术栈构建&#xff0c;旨在提升企业运营效率&#xff0c;优化资源配置&#xff0c;实现…

精准识别花生豆:基于EfficientNetB0的深度学习检测与分类项目

精准检测花生豆&#xff1a;基于EfficientNet的深度学习分类项目 在现代农业生产中&#xff0c;作物的质量检测和分类是确保产品质量的重要环节。针对花生豆的检测与分类需求&#xff0c;我们开发了一套基于深度学习的解决方案&#xff0c;利用EfficientNetB0模型实现高效、准…

使用套接字创建一个服务端,创建一个客户端然后相互通讯

以下是对上述代码的详细解释&#xff1a; #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h>#include <stdio.h> #include <stdlib.h> #include <string.h&…

ES 磁盘使用率检查及处理方法

文章目录 1. 检查原因2. 检查方法3. 处理方法3.1 清理数据3.2 再次检查磁盘使用率 1. 检查原因 磁盘使用率在 85%以下&#xff0c;ES 可正常运行&#xff0c;达到 85%及以上会影响 PEIM 数据存储。 在 ES 磁盘分配分片控制策略中&#xff0c;为了保护数据节点的安全&#xff0…

[cg] android studio 无法调试cpp问题

折腾了好久&#xff0c;native cpp库无法调试问题&#xff0c;原因 下面的Deploy 需要选Apk from app bundle!! 另外就是指定Debug type为Dual&#xff0c;并在Symbol Directories 指定native cpp的so路径 UE项目调试&#xff1a; 使用Android Studio调试虚幻引擎Android项目…

Flutter中添加全局防护水印的实现

随着版权意识的加强&#xff0c;越来越多的应用开始在应用内部增加各种各样的水印信息&#xff0c;防止核心信息泄露&#xff0c;便于朔源。 效果如下&#xff1a; 在Flutter中增加全局水印的方式&#xff0c;目前有两种实现。 方案一&#xff0c;在native层添加一个遮罩层&a…

MQTT——客户端安装使用(图文详解)

目录 一. 下载安装MQTT 1. 下载MQTT 2. 安装MQTT 二. MQTT客户端使用 1. 连接MQTT服务 2. MQTT发布消息 3. MQTT 消息订阅 4. 断开MQTT服务器连接 三. 使用Jmeter给MQTT发数据 一. 下载安装MQTT 1. 下载MQTT &#xff08;1&#xff09;官网下载地址&#xff1a;MQTTX…

2- 位段式结构体

文章目录 1 结构体内存对齐2 位段式结构体2.1 格式2.2 成员类型2.3 空间开辟2.4 示例2.4.1 示例12.4.2 示例2 1 结构体内存对齐 首成员对齐规则 结构体的第一个成员从偏移量为0的地址处开始存放&#xff0c;即与结构体的首地址对齐。 其他成员对齐规则 其他成员变量的存放地址…

Milvus×EasyAi:如何用java从零搭建人脸识别应用

如何从零搭建一个人脸识别应用&#xff1f;不妨试试原生Java人工智能算法&#xff1a;EasyAi Milvus 的组合拳。 本文将使用到的软件和工具包括&#xff1a; EasyAi&#xff1a;人脸特征向量提取Milvus&#xff1a;向量数据库用于高效存储和检索数据。 01. EasyAi&#xff1a;…

AWS K8s 部署架构

Amazon Web Services&#xff08;AWS&#xff09;提供了一种简化的Kubernetes&#xff08;K8s&#xff09;部署架构&#xff0c;使得在云环境中管理和扩展容器化应用变得更加容易。这个架构的核心是AWS EKS&#xff08;Elastic Kubernetes Service&#xff09;&#xff0c;它是…

[Pro Git#2] 分支管理 | branch fix_bug , feature | 处理合并冲突

目录 一、Issue模板文件 二、Pull Requests模板文件 分支管理 1. 理解分支 2. 创建与管理分支 1. 切换分支与提交历史 2. 合并分支 3. 删除分支 4. 解决合并冲突 6. 查看分支合并情况 快速创建并切换分支 分支管理策略 分支合并模式 分支管理原则 日常开发环境 …

Acwing 基础算法课 数学知识 筛法求欧拉函数

【G09 筛法求欧拉函数】https://www.bilibili.com/video/BV1VP411p7Bs?vd_source57dbd16b8c7c2ad258cccce5966c5be8 闫总真是把听者当数学系转cs的来讲&#xff0c;菜逼完全听不懂&#xff0c;只能其他地再搜 欧拉函数 φ ( n ) \varphi(n) φ(n)&#xff1a;1~n中与n互质的数…