热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Rust之常用集合(一):向量(vector)

开发环境Windows10Rust1.64.0VSCode1.72.2项目工程这里继续沿用上次工程rust-demo常用集合Rust的标准库包括许多非常有用的数据结构&#x

开发环境


  • Windows 10
  • Rust 1.64.0

 


  •   VS Code 1.72.2 


项目工程

这里继续沿用上次工程rust-demo


常用集合

Rust的标准库包括许多非常有用的数据结构,称为集合。大多数其他数据类型表示一个特定的值,但是集合可以包含多个值。与内置数组和元组类型不同,这些集合指向的数据存储在堆上,这意味着数据量在编译时不需要知道,并且可以随着程序运行而增长或收缩。每一种收藏都有不同的功能和成本,选择一种适合你当前情况的收藏是一项你需要慢慢培养的技能。我们将讨论Rust程序中经常使用的三个集合:


  • vector允许你存储可变数量的相邻值。
  • string是字符的集合。
  • hash map允许您将一个值与一个特定的键相关联。这是一个更一般的数据结构的特殊实现,称为map

用向量存储值列表

我们要看的第一个集合类型是Vec,也称为vector。向量允许您在一个数据结构中存储多个值,该数据结构将所有值在内存中相邻放置。向量只能存储相同类型的值。当您有一个项目列表时,例如文件中的文本行或购物车中的项目价格时,它们非常有用。


创建向量

为了创建一个新的空向量,我们调用Vec::new函数

fn main() {let v: Vec = Vec::new(); // 创建vector
}

注意,我们在这里添加了一个类型注释。因为我们没有在这个向量中插入任何值,所以Rust不知道我们打算存储什么样的元素。这是很重要的一点。向量是使用泛型实现的;现在,我们知道标准库提供的Vec类型可以保存任何类型。当我们创建一个向量来保存一个特定的类型时,我们可以在尖括号中指定类型。在上例中,我们已经告诉Rust,v中的Vec将保存i32类型的元素。

更常见的是,您将创建一个带有初始值的Vec , Rust将推断出您想要存储的值的类型,因此您很少需要做这种类型注释。Rust方便地提供了vec!宏,它将创建一个新的向量来保存您给它的值。下例中创建了一个新的Vec,它包含值1、2和3。整数类型是i32,因为这是默认的整数类型,

fn main() {let v = vec![1, 2, 3]; // 带有初始值的vector
}

因为我们已经给出了初始i32值,Rust可以推断出v的类型是Vec,类型注释是不必要的。接下来,我们将看看如何修改向量。


更新向量

要创建一个向量,然后向其中添加元素,我们可以使用push方法,

fn main() {let mut v = Vec::new(); // mut关键字,创建可修改的向量vectorv.push(5); // 和其他语言类似,传入数据v.push(6);v.push(7);v.push(8);
}

和任何变量一样,如果我们希望能够改变它的值,我们需要使用mut关键字使它可变,如第3章所讨论的。我们放入的数字都是i32类型的,Rust从数据中推断出这一点,所以我们不需要Vec注释。


读取向量的元素

有两种方法可以引用存储在vector中的值:通过索引或者使用get方法。在下面的例子中,为了更加清晰起见,我们对这些函数返回的值的类型进行了注释。

fn main() {let v = vec![1, 2, 3, 4, 5]; // 定义向量let third: &i32 = &v[2]; // 通过索引获取向量中的值println!("The third element is {}", third);let third: Option<&i32> = v.get(2); // 通过get接口获取向量中的值match third {Some(third) => println!("The third element is {}", third),NOne=> println!("There is no third element."),}
}

编译运行

cargo run

  结果

 请注意这里的一些细节。我们使用索引值2来获取第三个元素,因为向量是由数字索引的,从零开始。使用&和[]给出了对索引值处元素的引用。当我们使用get方法并将索引作为参数传递时,我们得到一个可以与match一起使用的Option<&T>

Rust提供这两种方法引用元素的原因是,当您试图使用现有元素范围之外的索引值时,您可以选择程序的行为方式。作为一个例子,让我们看看当我们有一个包含五个元素的向量,然后我们尝试用每种技术访问索引为100的元素时会发生什么,

fn main() {let v = vec![1, 2, 3, 4, 5];let does_not_exist = &v[100]; // 获取索引为100的值let does_not_exist = v.get(100);
}

 编译运行

cargo run

 当我们运行这段代码时,第一个[]方法将导致程序崩溃,因为它引用了一个不存在的元素。如果试图访问超过vector末尾的元素,希望程序崩溃时,最好使用这种方法

get方法被传递一个在vector之外的索引时,它返回None而不会死机。如果在正常情况下偶尔会访问vector范围之外的元素,那么可以使用这个方法。然后你的代码将有逻辑来处理有Some(&element)或者None,如之前章节讨论的。例如,索引可能来自输入数字的人。如果他们不小心输入了一个太大的数字,程序得到一个None值,你可以告诉用户当前向量中有多少项,然后再给他们一次输入有效值的机会。这比因为一个打字错误而导致程序崩溃更容易操作!

当程序有一个有效的引用时,借用检查器执行所有权和借用规则以确保这个引用和任何其他对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);
}

编译运行

cargo run

编译错误 

 上例中的代码看起来应该是可行的:为什么对第一个元素的引用要关心向量末尾的变化?这个错误是由vector的工作方式造成的:因为vector将值彼此相邻地放在内存中,所以在vector的末尾添加新元素可能需要分配新的内存,并将旧的元素复制到新的空间,如果没有足够的空间将所有元素彼此相邻地放在vector当前存储的位置。在这种情况下,对第一个元素的引用将指向已释放的内存。借用规则防止程序在这种情况下终止


迭代向量中的值

为了依次访问vector中的每个元素,我们将遍历所有元素,而不是使用索引一次访问一个元素。下例中显示了如何使用一个for循环来获得对i32值的向量中的每个元素的不可变引用,并打印它们。

fn main() {let v = vec![100, 32, 57];for i in &v { // for循环println!("{}", i);}
}

编译运行

cargo run

 我们还可以迭代可变向量中每个元素的可变引用,以便对所有元素进行更改。下例中中的for循环将每个元素加50。

fn main() {let mut v = vec![100, 32, 57];for i in &mut v {*i += 50; // 每个数加 50println!("{}", i);}
}

编译运行

cargo run

 

 要改变可变引用所引用的值,我们必须在使用+=操作符之前使用* 接触引用操作符来获得I中的值。

由于借用检查器的规则,迭代一个向量,无论是不变的还是可变的,都是安全的。如果我们试图在上例中的for循环体中插入或删除项目,我们会得到一个类似于上述其中代码的编译错误。对for循环持有的向量的引用防止了对整个向量的同时修改。


使用枚举存储多种类型

向量只能存储相同类型的值。这可能不方便;肯定有需要存储不同类型的项目列表的用例。幸运的是,一个枚举的变体是在同一个枚举类型下定义的,所以当我们需要一个类型来表示不同类型的元素时,我们可以定义并使用一个枚举

例如,假设我们想从电子表格的一行中获取值,该行中的一些列包含整数、一些浮点数和一些字符串。我们可以定义一个枚举,它的变量包含不同的值类型,所有的枚举变量都被认为是相同的类型:枚举的类型。然后我们可以创建一个向量来保存这个枚举,最终保存不同的类型。

fn main() {enum SpreadsheetCell { // 枚举Int(i32),Float(f64),Text(String),}let row = vec![ // 将枚举类型存入向量SpreadsheetCell::Int(3),SpreadsheetCell::Text(String::from("blue")),SpreadsheetCell::Float(10.12),];
}

Rust需要知道在编译时vector中有哪些类型,这样它就能准确地知道在堆中需要多少内存来存储每个元素。我们还必须明确在这个向量中允许什么类型。如果Rust允许vector保存任何类型,那么有可能一个或多个类型会导致对vector元素执行的操作出错。使用枚举加match表达式意味着Rust将确保在编译时处理每一种可能的情况。

如果你不知道一个程序在运行时将在vector中存储的所有类型,enum技术就不起作用。

既然我们已经讨论了使用vectors的一些最常见的方法,可以查看Rust API文档,了解标准库在Vec上定义的许多有用的方法。例如,除了push之外,pop方法还移除并返回最后一个元素。


删除向量会删除其元素

像任何其他struct一样,向量在超出范围时被释放,

fn main() {{let v = vec![1, 2, 3, 4];// do stuff with v} // <- v goes out of scope and is freed here // 有效范围
}

vector被丢弃时,它的所有内容也被丢弃,这意味着它保存的整数将被清除。借用检查器确保仅当向量本身有效时,才使用对向量内容的任何引用。


本章重点


  • 向量vector概念及创建方法
  • 向量的使用 - 更新数据
  • 读取向量中的元素
  • 迭代向量中的值
  • 使用枚举存储多类型的数据


推荐阅读
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • 在Docker中,将主机目录挂载到容器中作为volume使用时,常常会遇到文件权限问题。这是因为容器内外的UID不同所导致的。本文介绍了解决这个问题的方法,包括使用gosu和suexec工具以及在Dockerfile中配置volume的权限。通过这些方法,可以避免在使用Docker时出现无写权限的情况。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • 本文介绍了南邮ctf-web的writeup,包括签到题和md5 collision。在CTF比赛和渗透测试中,可以通过查看源代码、代码注释、页面隐藏元素、超链接和HTTP响应头部来寻找flag或提示信息。利用PHP弱类型,可以发现md5('QNKCDZO')='0e830400451993494058024219903391'和md5('240610708')='0e462097431906509019562988736854'。 ... [详细]
  • 利用Visual Basic开发SAP接口程序初探的方法与原理
    本文介绍了利用Visual Basic开发SAP接口程序的方法与原理,以及SAP R/3系统的特点和二次开发平台ABAP的使用。通过程序接口自动读取SAP R/3的数据表或视图,在外部进行处理和利用水晶报表等工具生成符合中国人习惯的报表样式。具体介绍了RFC调用的原理和模型,并强调本文主要不讨论SAP R/3函数的开发,而是针对使用SAP的公司的非ABAP开发人员提供了初步的接口程序开发指导。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • 模板引擎StringTemplate的使用方法和特点
    本文介绍了模板引擎StringTemplate的使用方法和特点,包括强制Model和View的分离、Lazy-Evaluation、Recursive enable等。同时,还介绍了StringTemplate语法中的属性和普通字符的使用方法,并提供了向模板填充属性的示例代码。 ... [详细]
  • GreenDAO快速入门
    前言之前在自己做项目的时候,用到了GreenDAO数据库,其实对于数据库辅助工具库从OrmLite,到litePal再到GreenDAO,总是在不停的切换,但是没有真正去了解他们的 ... [详细]
  • 本文介绍了pack布局管理器在Perl/Tk中的使用方法及注意事项。通过调用pack()方法,可以控制部件在显示窗口中的位置和大小。同时,本文还提到了在使用pack布局管理器时,应注意将部件分组以便在水平和垂直方向上进行堆放。此外,还介绍了使用Frame部件或Toplevel部件来组织部件在窗口内的方法。最后,本文强调了在使用pack布局管理器时,应避免在中间切换到grid布局管理器,以免造成混乱。 ... [详细]
author-avatar
jp85201
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有