Rust之通用集合类型

这篇具有很好参考价值的文章主要介绍了Rust之通用集合类型。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

在Rust语言中包含了一系列被称为集合的数据结构。大部分的数据结构都代表着某个特定的值,但集合却可以包含多个值。与内置的数组与元组类型不同,这些集合将自己持有的数据存储在了堆上。这意味着数据的大小不需要在编译时确定,并且可以随着程序的运行按需扩大或缩小数据占用的空间。
被广泛使用的3个Rust集合:

  • 动态数组:可以连续地存储任意多个值;
  • 字符串:字符的集合;
  • 哈希映射:可以将值关联到一个特定的键上,是映射的特殊实现。

1、使用动态数组存储多个值:

动态数组(Vec(T)),允许在单个数据结构中存储多个相同类型的值,这些值会彼此相邻地排布在内存中。

(1)、创建动态数组:

创建一个空的动态数组可以调用函数Vec::new
示例:

let v:Vec<i32> = Vec::new();

因为这是个空的动态数组,里面没有任何的值,所以Rust无法自动推导元素类型,就需要显式地增加一个类型标记。
在标准库中的Vec<T>是可以存储任意类型数据的,在实际的动态数组创建时,只要向数组内插入了数据,Rust便可以自行推导出想要存储的数据。Rust还提供了一个简化代码的宏vec!这个宏可以根据提供的数值来创建一个新的动态数组。
示例:

let v = vec![1,2,3];

这里创建了含有初始值1,2,3的动态数组,Rust根据初始值推断出想要存储的数据类型为i32。

(2)、更新动态数组:

在创建好的动态数组中添加元素,需要使用push。示例:

let mut v = Vec::new();

v.push(5);
v.push(6);
v.push(19);

(3)、销毁动态数组时也会销毁其中的元素:

动态数组一旦离开作用域就会被立即销毁。示例:

{
	let v = vec![1,2,3];
	//执行其他操作
}//V在这里离开作用域并随之被销毁

动态数组中的所有内容会随着动态数组的销毁而销毁,其持有的整数将被自动清理干净。

(4)、读取动态数组中的元素:

访问动态数组中的数据,可以通过索引或者get方法。示例:

let v = vec![1,2,3,4,5];
let third: &i32 = &v[2];
println!("The third element is {}",third};
match v.get(2){
	Some(third) => println!("The third element is {}",third},
	NONE => println!("There is no third element"),
}

注意:当使用索引值2获取数值时,获取的是第三个值,动态数组使用数字进行索引,索引值从零开始,使用&[]会直接返回元素的引用,而接收索引作为参数的get方法返回的则是一个Option<&T>。
同理,当越界访问元素时,直接使用[]会导致程序触发panic,而使用get方法时则会简单地返回None。
当一个变量持有了一个指向动态数组中首个元素的不可变引用,那么当向这个动态数组结尾添加元素时,会出现错误。示例:

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

这个错误是由动态数组的工作原理导致的:动态数组中的元素是连续存储的,插入新的元素后也许会没有足够的空间将所有的元素依次相邻地放下,这就需要分配一个新的内存空间,并将旧的元素移动到新的空间上去。但是当存在一个不可变引用,就会导致第一个元素的引用会因为插入新元素而指向被释放的内存上。

(5)、遍历动态数组中的值:

当需要依次访问动态数组的每一个元素的时候,可以直接遍历其所有的元素,而不需要使用索引一个一个地访问。示例:

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

同样也可以遍历可变的动态数组,获得元素的可变引用,并修改其中的值。示例:

let mut v = vec![1,2,3,4];
for i in &mut v{
	*i += 50;
}

为了使用+=运算符来修改可变引用指向的值,需要使用解引用运算符*来获得i绑定的值。

(6)、使用枚举来存储多个类型的值:

为了在动态数组中存储不同类型的元素,可以定义并使用枚举类型,因为枚举中的所有变体都被定义为了同一种枚举类型。示例:

enum SpreadsheetCell {
	 Int(i32),
	 Float(f64),
	 Text(String),
}
let row = vec![
	 SpreadsheetCell::Int(3),
	 SpreadsheetCell::Text(String::from("blue")),
	 SpreadsheetCell::Float(10.12),
];

为了计算出元素在堆上使用的存储空间,Rust需要在编译时确定动态数组的类型。使用枚举的另一个好处在于它可以显式地列举出所有可以被放入动态数组的值类型。假如Rust允许动态数组存储任意类型,那么在对动态数组中的元素进行操作时,就有可能会因为一个或多个不当的类型处理而导致错误。将枚举和match表达式搭配使用意味着,Rust可以在编译时确保所有可能的情形都得到妥当的处理。如果没有办法在编写程序时穷尽所有可能出现在动态数组中的值类型,那么就无法使用枚举。

2、使用字符串存储UTF-8编码的文本:

(1)、创建一个新的字符串:

可以使用new函数来创建一个新的字符串。示例:

let mut s = String::new();

这里创建了一个新的空字符串,之后可以将数据填入其中。
对含有初始数据的情况,在实现了Display trait的类型调用to_string方法去创建。示例:

let data = "initial contents";
let s = data.to_string();
//或者
let s = "initial contents".to_string();

同样也可以使用函数String::from来基于字符串字面量生成String。示例:

let s = String::from("initial contents");

(2)、更新字符串:

String的大小可以增减,其中的内容也可以修改。正如将数据推入其中时Vec内部数据所发生的变化一样。此外,我们还可以方便地使用+运算符或format! 宏来拼接String。

i、使用push_str或push向字符串中添加内容:

push_str只需要接收一个字符串切片作为参数,并不需要获得参数的所有权。示例:

let mut s1 = String::from("foo");
let s2 = "bar";
s1.push_str(s2);
println!("s2 is {}", s2);

在执行完,依旧可以打印s2的值。
push方法接收单个字符作为参数,并将它添加到String中。示例:

let mut s = String::from("lo");
s.push('l');

ii、使用+运算符或format! 宏来拼接字符串:

如果需要将两个已经存在的字符串组合在一起,可以使用+运算符。示例:

let s1 = String::from("Hello, ");
let s2 = String::from("world!");
let s3 = s1 + &s2; // 注意这里的s1已经被移动且再也不能被使用了,s2仅被引用

这段程序看起来是将s1和s2两个字符串复制后创建一个新的字符串,实际上是获取了S1的所有权,再将S2复制进去,最后将S1的所有权作为结果返回。
如果需要拼接多个字符串,可以使用format!宏来实现。示例:

let s1 = String::from("tic");
let s2 = String::from("tac");
let s3 = String::from("toe");
let s = format!("{}-{}-{}",s1,s2,s3);

format!宏会将结果包含在一个String中返回,并且不会夺取任何参数的所有权。

(3)、字符串索引:

在Rust中,字符串并不能向其他的编程语言那样通过索引去访问。
因为在Rust中,字符串是以UTF-8的形式存储的,每个字符都是由不定长的字节组成,所以通过常规的索引无法访问。

(4)、字符串切片:

如果需要一个字符串切片,则要在索引的[]中填写范围来指定所需要的字节内容,而不是在[]中填写一个单个数字。示例:

let hello = "3ΠpaB";
let s = &hello[0..4];

这段程序中,s是一个包含了字符串前4个字节的&str,如果每个字符都占据了两个字节,那么s中的内容就是3Π,如果使用&hello[0…1],那么程序就会报错。

3、在哈希映射中存储键值对:

哈希映射:HashMap<K,V>,存储了从K类型键到V类型值之间的映射关系。

(1)、创建一个新的哈希映射:

可以使用new来创建一个空的哈希映射,并通过insert方法来添加元素。示例:

use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"),10);
scores.insert(String::from("Yellow"),50);

这里的HashMap拥有类型为String的键,以及类型为i32的值。和动态数组一样,哈希映射也是同质的:它要求所有的键必须拥有相同的类型,所有的值也必须拥有相同的类型。
另一种构建哈希映射的方法是,在一个由键值对组成的元组动态数组上使用collect方法。示例:

use std::collections::HashMap;
let teams = vec![String::from("Blue"),String::from("Yellow")];
let initial_scores = vec![10,50];
let scores: HashMap<_,_> = teams.iter().zip(initial_scores.iter()).collect();

这里的类型标记HashMap<_,_>不能省略,因为collect可以作用于许多不同的数据类型,如果不指明类型的话,Rust就无法知道想要的具体类型。

(2)、哈希映射与所有权:

对于实现了Copy trait的类型,例如i32,它们的值会被简单地复制到哈希映射中,而对于String这种持有所有权的值,其值将会转移且所有权会被转移给哈希映射。示例:

use std::collections::HashMap;
let field_name = String::from("Favorite color");
let field_value = String::from("Blue");

let mut map = HashMap::new();
map.insert(filed_name,filed_value);
//filed_name和filed_value在这里开始失效

如果是将值的引用插入到哈希映射中,那么这些值是不会被移动到哈希映射中,这些引用所指向的值必须要保证,在哈希映射有效时,自己也是有效的。

(3)、访问哈希映射中的值:

可以通过将键传入到get方法来获取哈希映射中的值。示例:

use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"),10);
scores.insert(String::from("Yellow"),50);

let team_name = String::from("Blue");
let score = scores.get(&team_name);

因为get返回的是一个Option<&V>,所以这里的结果也被装到了Some里,如果哈希映射中没有键所对应的值,那么就会返回None。
同样可以使用for循环来遍历哈希映射中所有的键值对。示例:

use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"),10);
scores.insert(String::from("Yellow"),50);
for (key,value) in &scores{
	println!("{}:{}",key,value);
}

(4)、更新哈希映射:

i、覆盖旧值:

当将一个键值对插入到哈希映射后,接着使用同样的键并配以不同的值来继续插入,之前的键所关联的值就会被替换掉。示例:

use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"),10);
scores.insert(String::from("Blue"),25);
println!("{:?},scores);

这段程序会打印出{"Blus":25}

ii、只有键没有对应值时插入数据:

在实际工作中,常常需要检测一个键是否存在对应值,如果不存在,则为它插入一个值。哈希映射中提供了一个被称为entry的专用API来处理这种情形,它接收我们想要检测的键作为参数,并返回一个叫作Entry的枚举作为结果。这个枚举指明了键所对应的值是否存在。示例:

use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.entry(String::from("Yellow")).or_insert(50);
scores.entry(String::from("Blue")).or_insert(50);
println!("{:?}", scores);

iii、基于旧值来更新值:

哈希映射的另外一个常见用法是查找某个键所对应的值,并基于这个值来进行更新。文章来源地址https://www.toymoban.com/news/detail-614297.html

use std::collections::HashMap;
let text = "hello world wonderful world";
let mut map = HashMap::new();
for word in text.split_whitespace() {
	 let count = map.entry(word).or_insert(0);
	 *count += 1;
}
println!("{:?}", map);

到了这里,关于Rust之通用集合类型的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Rust 笔记:Rust 语言中哈希结构(哈希映射,HashMap)、集合(哈希集,HashSet)及其使用

    Rust 笔记 Rust 语言中映射(HashMap)与集合(HashSet)及其用法 作者 : 李俊才 (jcLee95):https://blog.csdn.net/qq_28550263?spm=1001.2101.3001.5343 邮箱 : 291148484@163.com 本文地址 :https://blog.csdn.net/qq_28550263/article/details/130876735 【介绍】:本文介绍 Rust 中哈希结构相关概念及其使用。在 R

    2024年02月09日
    浏览(38)
  • Rust语言精讲:数据类型全解析

    大家好!我是lincyang。 今天,我们将深入探讨Rust语言中的数据类型,这是理解和掌握Rust的基础。 Rust语言数据类型概览 Rust是静态类型语言,所有变量类型在编译时确定。Rust的数据类型分为两类:标量类型和复合类型。 标量类型 标量类型是单一值的类型,包括整型、浮点型

    2024年02月05日
    浏览(38)
  • rust学习-类型转换

    根据其他类型生成自己 把其他类型转为目的类型 要把任何类型转换成 String,只需要实现那个类型的 ToString trait 实现fmt::Display trait,它会自动提供 ToString,并且还可以用来打印类型 用 parse 函数 只要对目标类型实现了 FromStr trait,就可以用 parse 把字符串转换成目标类型 “涡

    2024年02月11日
    浏览(22)
  • Rust 学习笔记 - 流程控制 与 Range 类型

    任何一门编程语言几乎都脱离不了:变量、基本类型、函数、注释、循环、条件判断,这是一门编程语言的语法基础,只有当掌握这些基础语法及概念才能更好的学习 Rust。 if 语句在其他语言中很常见,这里不再多做解释,看注释即可。 if  表达式也支持 if...else if...else 语句

    2024年02月20日
    浏览(27)
  • C语言学习:8、深入数据类型

    C语言中,如果需要用的整数大于int类型的最大值了怎么办? 我们知道int能表示的最大数是2147483647,最小的数是-2147483648,为什么? 因为字32位系统中,寄存器是32位的,寄存器中最高位表示符号位,0表示整数,1表示负数; 所以32位系统中int的最大值可以表示为0111 1111 1111

    2024年02月09日
    浏览(29)
  • 【AI理论学习】语言模型Performer:一种基于Transformer架构的通用注意力框架

    Performer是一种用于高效处理自注意力机制(Self-Attention)的神经网络架构 。自注意力机制在许多自然语言处理和计算机视觉任务中

    2024年02月09日
    浏览(42)
  • C语言——自定义类型结构体_学习笔记

    结构体是一种用户自定义的数据类型,可以包含 多个不同类型的变量 。通过使用结构体,我们可以将相关联的数据组织在一起,便于管理和使用。 在C语言中,结构体(struct)指的是一种数据结构,是C语言中聚合数据类型的一类。 结构体可以包含多个不同类型的数据成员,例

    2024年02月07日
    浏览(28)
  • C语言入门教程,C语言学习教程(第三部分:C语言变量和数据类型)二

    前面我们多次提到了字符串,字符串是多个字符的集合,它们由 \\\" \\\" 包围,例如 \\\"http://c.biancheng.net\\\" 、 \\\"C语言中文网\\\" 。字符串中的字符在内存中按照次序、紧挨着排列,整个字符串占用一块连续的内存。 当然,字符串也可以只包含一个字符,例如 \\\"A\\\" 、 \\\"6\\\" ;不过为了操作方

    2024年01月17日
    浏览(40)
  • Rust之通用编程

    在Rust语言中,变量默认是不可变的,所以一旦变量被绑定到某个值上面,这个值就再也无法被改变。 可以通过在声明的变量名称前添加 mut 来使其可变。除了使变量的值可变, mut 还会向阅读代码的人暗示其他代码可能会改变这个变量的值。 a、整数: 在Rust中的整数类

    2024年02月16日
    浏览(30)
  • Rust通用编程概念

    变量与可变性 在Rust中,声明变量使用let,并且默认情况下,声明的变量是不可变的,要使变量可变需要在声明变量时,在变量前面加上mut。如下: 如果将上述代码中的mut去掉,那么在编译代码时就会报错,报错结果就是不能对不可变的变量进行二次赋值

    2024年02月08日
    浏览(29)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包