很久没有挖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<u8>,Vec是向量集合的关键字,我们在后面会介绍。String类型由三个部分组成,分别是:指向堆中字节序列的指针,记录堆中字节序列的长度和堆分配的容量。通过一段代码也许你很有更深的理解。

fn main() {     let mut a = 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获取的指针是不一样的。

rust06-1

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

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

创建字符串

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

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

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

修改字符串

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

fn main() {     let mut hello = 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);      let left = "Hello, ".to_string();     let right = "World".to_string();