很久没有挖Rust的坑啦,今天来挖一些排列整齐的坑。没错,就是要介绍一些集合类型的数据类型。“鳞次栉比”这个标题是不是显得很有文化?

在Rust入坑指南:常规套路一文中我们已经介绍了一些基本数据类型了,它们都存储在栈中,今天我们重点介绍3种数据类型:string,vector和hash map。

String

String类型我们在之前的学习中已经有了较多的接触,但是没有进行过详细的介绍。有些有编程基础的同学可能不屑于学习String类型,毕竟它在所有编程语言中可以说是最常用的类型了,大家也都很熟悉了。对于有这种心理的同学,我想对他们说:我刚开始也是这样想的,直到后来我被编译器揍的满头包,才下定决心回来认真学习一下String类型。

Rust的字符串分为以下几种类型:str:表示固定长度的字符串

String:表示可增长的字符串

CStr:表示由C分配,被Rust借用的字符串,一般用于和C语言交互

CString:表示由Rust分配并且可以传递给C语言的字符串

OsStr:表示和操作系统相关的字符串,主要为了兼容Windows

OsString:OsStr的可变版本

Path:表示路径

PathBuf:是Path的可变版本

本文我们重点讨论前两种,因为它们是开发过程中最常用的,也是比较容易混淆的。对于str,我们常见的是它的引用类型,&str。如果你看过了Rust入坑指南:核心概念一文后,相信你已经了解了引用类型和Ownership的概念。也就是说String类型具有Ownership而&str没有。

在Rust中,String本质上是Vec\,Vec是向量集合的关键字,我们在后面会介绍。String类型由三个部分组成,分别是:指向堆中字节序列的指针,记录堆中字节序列的长度和堆分配的容量。通过一段代码也许你很有更深的理解。

fn main(){letmuta=String::from("foo");println!("{:p}",a.as_ptr());println!("{:p}",&a);assert_eq!(a.len(),3);a.reserve(10);assert_eq!(a.capacity(),13);}

在这段代码中我们可以看到,a.as_ptr()获取指针和&a获取的指针是不一样的。

这里我们解释一下,as_ptr获取到的指针是堆中字节序列的指针地址,而&a的地址是字符串变量在栈上的指针地址。另外,len()和capacity()方法得到的长度都是字节数量,而非字符数量。这里你可以自己动手试试中文字符的长度。

聊完了字符串的基本概念以后,相信你已经对Rust的字符串有了一个大概的认识。接下来我们就一起来看一看字符串的CRUD的方法吧。

创建字符串

话不多说,先来展示一下创建字符串的各种方法。

fn main(){letstring: String =String::new();letstring: String =String::from("hello rust");letstring: String =String::with_capacity(10);letstr: &'staticstr="Jackey";letstring: String =str.to_owned();letstring: String =str.to_string();}

我们比较常用的是前两种,下面介绍一下后面几个方法。with_capacity()是创建一个空字符串,参数表示在堆中分配的字节数。to_owned和to_string是演示了如何把&str类型转换成String类型。

修改字符串

Rust修改字符串的常用方法也有很多,例如在字符串后追加,连接两个字符串,更新字符串等。下面这段代码就展示了一些修改字符串的方法。

fn main(){letmuthello=String::from("Hello, ");hello.push('J');// 追加单个字符hello.push_str("ackey! ");//追加字符串println!("push: {}",hello);hello.extend(['M','y',' '].iter());//追加多个字符,参数为迭代器hello.extend("name".chars());println!("extend: {}",hello);hello.insert(0,'h');//类似于push,可以指定插入的位置hello.insert(1,'a');hello.insert(2,'!');hello.insert_str(0,"Haha");println!("insert: {}",hello);letleft="Hello, ".to_string();letright="World".to_string();letresult=left+&right;println!("+: {}",result);//使用+连接字符串时,第二个必须为引用letmutmessage="rust".to_string();//使用+=连接字符串时,字符串必须定义为可变message+="!";println!("+=: {}",message);lets=String::from("foobar");lets: String =s.chars().enumerate().map(|(_i,c)|{c.to_uppercase().to_string()}).collect();println!("update chars: {}",s);lets1=String::from("hello");lets2=String::from("rust");lets3=format!("{}-{}",s1,s2);println!("format: {}",s3);}

我们对上面的代码做一些补充的解释。

push和insert类似,带有_str的方法接收的参数是字符串,否则只能接收单个字符。insert可以指定插入的位置,而push只能在字符串末尾插入。

使用「+」连接字符串时,第一个参数是String类型,第二个则需要是引用类型&str。这类似于我们调用一个add方法,它的定义是这样的:

fn add(self,s: &str)-> String {

所以,第一个参数的ownership转移到了函数中,又通过返回结果传递出来。也就是说,在使用了+操作符之后,left已经没有ownership了。

字符串查找

在Rust中,字符串是不能根据位置来获取到指定字符的。也就是下面这段代码是编译不过的。

lets1=String::from("hello");leth=s1[0];

因为,Rust会认为这个0是指第一个字节,而Rust字符串中的字符可能占有多个字节(还记得前面我让你用中文字符实验代码吗?)所以,如果你单纯的想要获取一个字节,编译器不知道你是真的想要获取字节对应的数值,还是要获取那个字符。

我们在处理字符串时通常有以下方法:

fn main(){lethello="Здравствуйте";lets=&hello[0..4];println!("{}",s);letchars=hello.chars();forcinchars{println!("{}",c);}letbytes=hello.bytes();forbyteinbytes{println!("{}",byte);}letget=hello.get(0..1);letmuts=String::from("hello");letget_mut=s.get_mut(3..5);letmessage=String::from("hello-world");let(left,right)=message.split_at(6);println!("left: {}, right: {}",left,right);}

通常是使用字符切片,也可以使用chars方法获取到Chars迭代器,然后可以对每个字符进行单独处理。此外,使用get或get_mut方法也可以接收索引范围,返回指定的字符串切片。返回结果是Option类型,这是因为如果指定的索引返回不能返回完整字符,那么Rust就会返回None。这里也可以使用is_char_boundary方法来判断一个位置是否是非法边界。

最后,也可以使用split_at或split_at_mut方法来分割字符串。这要求分割的位置正好是字符边界位置,如果不是,程序就会崩溃。

删除字符串

Rust的标准库提供了一些删除字符串的方法,我们来演示一些:

fn main(){letmuthello=String::from("hello");hello.remove(3);println!("remove: {}",hello);hello.pop();println!("pop: {}",hello);hello.truncate(1);println!("truncate: {}",hello);hello.clear();println!("clear: {}",hello);}

结果如图:

remove方法用来删除字符串中的某个字符,其接收的参数是字符的起始位置,如果是不是某个字符的起始位置,会导致程序崩溃。

pop方法会弹出字符串末尾的字符,truncate方法是截取指定长度字符串,而clear方法则是用来清空字符串。

至此,关于Rust中的字符串的基本概念和CRUD我们都已经介绍完了,接下来我们再来看另一种集合类型Vector。

Vector

Vector是用来存储相同数据类型的多个数据一种数据类型。它的关键字是Vec。下面我们一起来看看向量的CRUD吧。

创建向量

fn main(){letv1: Vec=Vec::new();letv2=vec![1,2,3];}

上面这段代码演示了创建一个向量的两种方式,第一种是使用new函数来创建一个空的向量,由于没有添加元素,所以要显式的指定存储元素的类型。第二种是创建一个有初始值的向量集合,我们直接使用vec!宏,然后指定初始值即可,不需要指定向量中元素的数据类型,因为编译器可以自己推断出来。

更新向量

fn main(){letmutv=Vec::new();v.push(1);v.push(2);}

创建一个空的向量之后,如果我们想要增加元素,就可以直接使用push方法,向末尾追加元素。

删除向量

fn main(){letmutv=Vec::new();v.push(1);v.push(2);v.push(3);v.pop();foriin&v{println!("{}",i);}}

删除单个元素可以使用pop方法,而要删除整个向量,只能像其他结构体一样,到其ownership失效。

读取向量元素

fn main(){letv=vec![1,2,3,4,5];letthird: &i32 =&v[2];println!("The third element is {}",third);matchv.get(2){Some(third)=>println!("The third element is {}",third),None=>println!("There is no third element."),}letv=vec![100,32,57];foriin&v{println!("{}",i);}}

当你需要读取单个指定元素时,有两种方法可以用,一种是使用[],另一种是使用get方法。两种方法的区别是:第一种返回的是元素的类型,而get返回的是Option类型。如果你指定的位置越界了,那么使用第一种方法程序会直接崩溃,而使用第二种方法则会返回None。

此外,还可以通过遍历向量的形式来读取元素。如果想要存储不同类型的数据,我们可以借助枚举类型。

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

HashMap

HashMap存储了KV结构的数据,各个Key必须是同一种类型,各个Value必须是同一种类型。由于HashMap是三种集合类型中使用最少的,所以在使用之前,需要手动引入进来

usestd::collections::HashMap;

创建HashMap

首先我们来了解一下如何创建一个新的Hash Map并增加元素。

usestd::collections::HashMap;fn main(){letfield_name=String::from("Favorite color");letfield_value=String::from("Blue");letmutmap=HashMap::new();map.insert(field_name,field_value);}

注意,在使用insert方法时,field_name和field_value都会失去所有权。那如何再使用它们呢?我们只能从Hash Map中再拿出来。

访问Hash Map的数据

usestd::collections::HashMap;fn main(){letfield_name=String::from("Favorite color");letfield_value=String::from("Blue");letmutmap=HashMap::new();map.insert(field_name,field_value);letfavorite=String::from("Favorite color");letcolor=map.get(&favorite);matchcolor{Some(x)=>println!("{}",x),None=>println!("None"),}}

可以看到,我们使用get可以获取到指定Key的值,get方法返回的是Option类型,如果没有指定的Value,则会返回None。此外,也可以使用for循环来遍历Hash Map。

usestd::collections::HashMap;fn main(){letmutscores=HashMap::new();scores.insert(String::from("Blue"),10);scores.insert(String::from("Yellow"),50);for(key,value)in&scores{println!("{}: {}",key,value);}}

更新Hash Map

当我们向同一个Key insert值时,旧的值就会被覆盖。如果只想要在Key不存在时插入,则可以使用entry。

usestd::collections::HashMap;fn main(){letmutscores=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);}

总结

今天带大家一起挖了三个坑,string,vector和hash map,分别介绍了每种数据类型的CRUD。对string的介绍占了比较大的篇幅,因为它是最常用的数据类型之一。当然这部分的相关知识还有很多,欢迎大家和我一起学习交流。

rust的矿坑_Rust入坑指南:鳞次栉比相关推荐

  1. 丅rust是什么意思_Rust入坑指南:亡羊补牢

    如果你已经开始学习Rust,相信你已经体会过Rust编译器的强大.它可以帮助你避免程序中的大部分错误,但是编译器也不是万能的,如果程序写的不恰当,还是会发生错误,让程序崩溃.所以今天我们就来聊一聊Ru ...

  2. python入坑指南_Rust入坑指南:万物初始

    有没有同学记得我们一起挖了多少个坑?嗯-其实我自己也不记得了,今天我们再来挖一个特殊的坑,这个坑可以说是挖到根源了--元编程. 元编程是编程领域的一个重要概念,它允许程序将代码作为数据,在运行时对代码 ...

  3. Rust 入坑指南:鳞次栉比 | CSDN 博文精选

    作者 | Jackyzhe 责编 | 屠敏 出品 | CSDN(ID:CSDNnews) 很久没有挖Rust的坑啦,今天来挖一些排列整齐的坑.没错,就是要介绍一些集合类型的数据类型."鳞次栉 ...

  4. Rust入坑指南:鳞次栉比

    很久没有挖Rust的坑啦,今天来挖一些排列整齐的坑.没错,就是要介绍一些集合类型的数据类型."鳞次栉比"这个标题是不是显得很有文化? 在Rust入坑指南:常规套路一文中我们已经介绍 ...

  5. Rust入坑指南:齐头并进(上)

    我们知道,如今CPU的计算能力已经非常强大,其速度比内存要高出许多个数量级.为了充分利用CPU资源,多数编程语言都提供了并发编程的能力,Rust也不例外. 聊到并发,就离不开多进程和多线程这两个概念. ...

  6. Rust入坑指南:朝生暮死

    今天想和大家一起把我们之前挖的坑再刨深一些.在Java中,一个对象能存活多久全靠JVM来决定,程序员并不需要去关心对象的生命周期,但是在Rust中就大不相同,一个对象从生到死我们都需要掌握的很清楚. ...

  7. Rust入坑指南:亡羊补牢

    如果你已经开始学习Rust,相信你已经体会过Rust编译器的强大.它可以帮助你避免程序中的大部分错误,但是编译器也不是万能的,如果程序写的不恰当,还是会发生错误,让程序崩溃.所以今天我们就来聊一聊Ru ...

  8. Rust入坑指南:核心概念

    如果说前面的坑我们一直在用小铲子挖的话,那么今天的坑就是用挖掘机挖的. 今天要介绍的是Rust的一个核心概念:Ownership.全文将分为什么是Ownership以及Ownership的传递类型两部 ...

  9. 发布开源框架到CocoaPods入坑指南

    个人原文博客地址: 发布开源框架到CocoaPods入坑指南 在开发过程中一定会用到一些第三方框架, 只要安装了CocoaPods, 然后通过pod install命令, 就可以集成框架到项目中了 可 ...

最新文章

  1. Oracle 12c(12.1.0.5) oem agent silent install(静默安装agent)
  2. 震惊!原来Android OpenGL ES可以这样用,实现 (水波纹)涟漪效果超惊艳!
  3. 没有bug队——加贝——Python 45,46
  4. linux做完sftp端口分离后ftp,Linux 中实现文件传输服务(FTP、SFTP)
  5. Nitro-LM保护Flex/AIR应用的安全
  6. uipath 收邮件_UIpath 循环读取IMAP邮件,并保存附件
  7. 多字节编码与Unicode码的区别 内码
  8. 戴尔计算机亮度如何调整,官方数据:如何调整Dell显示器的亮度
  9. mysql导入GP_GP数据库gpload数据导入详细操作
  10. 机器学习--sklearn(决策树)
  11. 无法将数值apsdaemon写入键
  12. https证书格式转换(cer转bks)
  13. 【Android开发】Android基本UI组件
  14. littlefs系列:Technical Specification
  15. html网页添加背景音乐
  16. XenDesktop 5 .VS.View 4.5
  17. vue中的生命周期函数都在什么时候执行?
  18. 基于SSM的宠物管理系统
  19. 关系型数据库分库分表中间件之选型
  20. python123外汇兑换计算器_Python之计算器

热门文章

  1. Scrutiny 10 for Mac(网站分析检测工具)
  2. 利尔达与紫光展锐、中国电信等行业伙伴共筑NB-IoT安全生态
  3. 如何通过发新浪微博关闭电脑
  4. php中表单输出成绩,js内置对象处理_打印学生成绩单的简单实现
  5. POJ 1418 Viva Confetti(Japan 2002 Kanazawa)
  6. 官网下载Spring的jar包教程
  7. 中国女式内衣市场销售态势分析与投资前景研究报告2022-2028年
  8. 2BizBox免费ERP API初体验
  9. mysql数据库 purge_MySQL 研究innodb_max_purge_lag分享
  10. 【已解决】无法初始化设备 PRN