前言
上一篇,介绍了Rust语言的
-
结构体的函数和方法实现
-
枚举的函数和方法实现
-
模块简介
这一篇,我们介绍一下Rust中的复杂数据类型
-
集合体
-
数组
-
元组
-
向量
-
哈希
-
切片
-
-
迭代器
集合体(Collections)
在编程实践中,显然会遇到处理“组团”数据的情况,这自然需要复杂数据类型来呼应,那么集合体就出现了,Rust提供了多种这样的内建类型。
本节,首先看下数组和元组。然后,看下标准库中的动态集合类型,主要介绍向量vectors(列表list)和哈希图hashmaps(键/值,key/value)。最后,引出切片slices,以其进入复杂数据的内部一看究竟。
数组(Arrays)
先从Rust数组开始,与其他语言的数组类似,要求定长和同类型,用[T, N]表示,其中T为任意类型,N为数组中元素的个数,不能是一个变量,建议使用常量usize值。
看下这个代码实例:
// arrays.rs
fn main() {
let numbers: [u8; 10] = [1, 2, 3, 4, 5, 7, 8, 9, 10, 11];
let floats = [0.1f64, 0.2, 0.3];
println!("Number: {}", numbers[5]);
println!("Float: {}", floats[2]);
}
以上代码结果为:
第4,5行分别给出了两种初始化数组的方式,第一种是标准方式,第二种是把类型放在一个元素的位置并加上前缀指定类型,显得很灵活。
元组(Tuples)
与数组不同的是,元组中可以存放不同类型的数据,因此是一种异质性的集合体,在为函数传参或者做返回值方面用处颇大
// tuples.rs
fn main() {
let num_and_str: (u8, &str) = (40, "Have a good day!");
println!("{:?}", num_and_str);
let (num, string) = num_and_str;
println!("From tuple: Number: {}, String: {}", num, string);
}
以上代码结果为
可见,第4-5行,定义并提取一个元组的值,第6-7行,对元组进行分解赋值。
向量(Vectors)
比起数组,向量的好处,是不必提前定义内容和长度,这是一种与时俱进的类型,显然是动态的,在栈上分配空间,可以通过调用Vec::new构造函数或使用Vec ![]宏进行创建。
好,上代码:
// vec.rs
fn main() {
let mut numbers_vec: Vec<u8> = Vec::new();
numbers_vec.push(1);
numbers_vec.push(2);
let mut vec_with_macro = vec![1];
vec_with_macro.push(2);
let _ = vec_with_macro.pop(); // value ignored with `_`
let message = if numbers_vec == vec_with_macro {
"They are equal"
} else {
"Nah! They look different to me"
};
println!("{} {:?} {:?}", message, numbers_vec, vec_with_macro);
}
简单分析一下,这里分别用两种方式创建向量:第4行的number_vec 和 第8行的vec_with_macro ;而后利用push()和pop()进行增删数据。
在这里还有诸多用法,
https://doc.rust-lang.org/std/vec/struct.Vec.html
读者可以自己再跑跑。实际上向量也可以利用for 循环来体现其迭代器性质。
上述代码结果如下
哈希(Hashmaps)
Rust不会忘记提供映射格式来存储键值数据的,相关功能来自于标准库std::collections模块,名为HashMap,通过HashMap::new函数来创建。
看下代码:
// hashmaps.rs
use std::collections::HashMap;
fn main() {
let mut fruits = HashMap::new();
fruits.insert("apple", 3);
fruits.insert("mango", 6);
fruits.insert("orange", 2);
fruits.insert("avocado", 7);
for (k, v) in &fruits {
println!("I got {} {}", v, k);
}
fruits.remove("orange");
let old_avocado = fruits["avocado"];
fruits.insert("avocado", old_avocado + 5);
println!("\nI now have {} avocados", fruits["avocado"]);
}
我们读一下:
-
第3行,载入需要的模块std::collections::HashMap
-
第6行,创建新的哈希图变量,fruits
-
第7-10行,为该变量赋键值,使用inser方法
-
第11-13行,打印赋值结果,涉及for循环,这里面是循环变量是元组(k,v),分别对应keys()和values()两种方法,面向引用变量&fruits进行迭代
-
第15行,删除一个键,一对键值同时删除
-
第16行,创建一个简单变量old_avocado,取得avocado在fruits中的数值
-
第17行,为fruits中的avocado插入新值
-
第18行,打印修改后的fruits["avocado"]数值
上述代码结果如下
一般而言,用于对HashMap类型的键进行散列的算法基于Robin hood开放寻址方案,但可以根据用例和性能使用自定义散列器替换
切片(Slices)
切片是一种”瞥见“集合类型数据内容的通用方法,大多数用例在于获得对集合类型中特定范围项的只读访问。切片基本上是一个指针或引用,指向由其他变量现所拥有的集合类型中的一个连续区间。
在底层,切片是指向堆栈或堆中某处数据的胖指针(fat pointer),这意味着切片,除了包含指向该数据的指针,还拥有指向多少数据的信息。切片用&[T]表示,其中T是类型,用法与数组很相似。
我们看下代码:
// slices.rs
fn main() {
let mut numbers: [u8; 4] = [1, 2, 3, 4];
{
let all: &[u8] = &numbers[..];
println!("All of them: {:?}", all);
}
{
let first_two: &mut [u8] = &mut numbers[0..2];
first_two[0] = 100;
first_two[1] = 99;
}
println!("Look! I can modify through slices: {:?}", numbers);
}
我们读一下代码:
-
第4行,创建一个可变绑定类型的数组变量 numbers
-
第6行,创建&[u8]类型的切片,并使用&numbers指向数组number
-
[..]意味着全部引用该数组的数据
-
之所以使用&是由于切片不能把数组数据拿来,只能引用,根源在于切片是unsized types,而这又是一个后续要详细谈的内容,读者莫急
-
-
第11行,创建切片引用该数组的前两个位置
-
第12-16行,通过切片改变原来数组的值,打印结果,成功
上述代码结果如下
不知道读者是否看到,代码中的第5,8,10,14行中有两组大括号,用来与不可变绑定变量进行区隔,否则不会通过编译的,这一点依然要到后续篇章才能讲清楚。
迭代器(Iterators)
迭代器不是什么新概念,其出现是以一种高效的方式过一下集合体中的元素,很多语言中都有,比如Python的iter(some_list) 和C++的 vector.begin() 。其优势体现在
-
提供一种优雅高级的遍历集合体数据的方式,而不用手写for循环了
-
迭代器不会直接读取全部数据,而是采取懒惰(lazy)的方式,体现在
-
如果只需要一个数据,那么就只访问该条数据
-
还可以与多个转换操作(multiple transformation operations)链接在一起,
-
比如根据条件筛选元素,并且在需要时才对转换进行计算
-
提供next()方法,为读取下一条做准备
-
-
在Rust中,迭代器可以是实现其特性的任何类型,然后可以在for循环中使用此类型遍历其项,并实现在大多数标准库集合类型上,如Vector、HashMap、BTreeMap等等,也可以实现在自定义的类型上。
处理Rust中的集合类型时,迭代器是常用器械。实际上,Rust的for循环就被退化并隐藏为一个常规的包含next方法的match表达式,调用遍历对象。此外,可以通过调用iter()或into_iter()将大多数集合类型转换为迭代器。
后边篇章还要结合新的知识点来进一步介绍迭代器的内容,本篇先到这里,很庆幸吧,这一节没有代码。
结语
本篇讲过了集合体和迭代器,里面蕴含了一些尚未揭开的谜团,请读者耐心一下,后面都会一一说明。
下一篇会先讲一下Rust中的项目管理。
主要参考和建议读者进一步阅读的文献
https://doc.rust-lang.org/book
1.Rust编程之道,2019, 张汉东
2.The Complete Rust Programming Reference Guide,2019, Rahul Sharma,Vesa Kaihlavirta,Claus Matzinger
3.Hands-On Data Structures and Algorithms with Rust,2018,Claus Matzinger
4.Beginning Rust ,2018,Carlo Milanesi
5.Rust Cookbook,2017,Vigneshwer Dhinakaran