Rust标准库包含了一系列非常有用的被称为集合的数据结构。本篇文章我们主要介绍:动态数组、字符串和哈希映射。
动态数组:Vec
。动态数组允许你在单个数据结构中存储多个相同类型的值,这些值会彼此相邻地排布在内存中。动态数组非常合适在需要存储一系列相同类型值的场景中使用。
Rust创建动态数组有以下几个方法:
Vec::new()
:创建一个空动态数组Vec::with_capacity(10)
:创建一个指定容量的动态数组vec![]
:创建一个持有初始值的数组vec![n; m]
:创建并初始化vec
,共m
个元素,每个元素都初始化为n
let vec: Vec<i32> = Vec::new(); // []
let vec1: Vec<i32> = Vec::with_capacity(10); // []
let vec2 = vec![1, 2, 3]; // [1,2,3]
let vec3 = vec![1; 5]; // [1,1,1,1,1]
如果需要使vec
可以修改,需要使用mut
关键字。例子:
let mut vec = Vec::new(); // 此时可以不用标明数据类型,当存入数据的时候,编译器可以推到出数据类型。
vec.push(10);
vec.push(11);
vec[1] = 12;
println!("{:?}", vec); // 修改前 [10, 11]
vec[1] = 12; // 修改
println!("{:?}", vec); // 修改后 [10, 12]
如果访问数组的数据,可以使用索引来访问vec
中的元素。索引越界之后,将在运行时panic
报错。
let v = vec![5, 6, 7];
let idx: usize = 1
let i = v[idx]; // 5
再有如果我们想引用动态数组中元素可以使用引用和get
方法:
let v = vec![5, 6, 7];
let i = v[1]; // i32
println!("{}", i); // 6
let j = &v[2]; // &i32
println!("{}", j); // 7
vec
的get
方法有两种:
get
:获取指定索引处的元素引用或范围内元素的引用,如果索引越界,返回None
get_mut
:获取元素的可变引用或范围内元素的可变引用,如果索引越界,返回None
let mut v = vec![5, 6, 7];
let a = v.get(1); // Option<&i32>
println!("{}", a.unwrap()); // 6
v.get_mut(1).map(|e| {
*e = 10
});
v.get(1).map(|data| {
println!("{}", data) // 10
});
数组遍历可以使用for in
:
let v = vec![5, 6, 7];
for i in &v {
println!("{}", i);
}
如果想在遍历的时候修改数组:
let mut v = vec![5, 6, 7];
for i in &mut v {
*i += 50;
}
从上面的例子我们可以知道,Vec只能存储一种类型的数据,那么如何突破这点呢?此时就可以用枚举突破,上例子:
enum SpreadsheetCell {
Int(i32),
Float(f64),
Text(String),
}
let v = vec![
SpreadsheetCell::Int(3),
SpreadsheetCell::Float(20.23),
SpreadsheetCell::Text(String::from("hello world")),
];
接着我们可以看一下,vec
常用的一些方法:
方法 | 描述 |
---|---|
len() | 返回vec 的元素数量 |
is_empty() | vec 是否为空 |
push() | 在vec 尾部插入元素 |
pop() | 删除并返回vec 尾部的元素,为空则返回None |
insert() | 在指定位置插入元素 |
remove() | 删除指定索引处的元素并返回被删除的元素,索引越界将panic 报错退出 |
clear() | 清空vec |
append() | 将另一个vec 中所有元素追加移入vec 中,移动后另一个vec 为空 |
truncate() | 将vec 截断到指定长度,多与的元素被删除 |
retain() | 保留满足条件的元素,即删除不满足条件的元素 |
drain() | 删除vec 中指定范围的元素,同时返回一个迭代该范围所有元素的迭代器 |
split_off() | 将vec 从指定索引处切分成两个vec ,索引左边(不包括索引位置处)的元素保留在原vec 中,索引右边(包括索引位置处)的元素在返回的vec 中 |
更多的方法操作可以看:https://www.rustwiki.org.cn/zh-CN/std/vec/struct.Vec.html
Rust
中的字符串使用了UTF-8
编码。Rust在语言核心部分只有一种字符串类型,那就是字符串切片(str
),它通常以借用(&str
)的形式出现。
字符串是比很多开发者所理解的更为复杂的数据结构。加上
UTF-8
的不定长编码等原因,Rust
中的字符串并不如其它语言中那么好理解。
基本上我们可以使用:new
和to_string
方法进行创建:
let a = "hello world"; // &str
let b = a.to_string(); // String
let c = String::from("hello world"); // String
let d = "hello world".to_string(); // String
更新字符串有三种方法,一种是使用push
方法,如下:
let mut a = "hello world".to_string();
a.push_str(" rust");
println!("{}", a); // hello world rust
a.push('6');
println!("{}", a); // hello world rust6
另一种是使用+
运算符,这里可以看作是push_str
函数
let s1 = String::from("hello ");
let s2 = String::from("world");
let s3 = s1 + &s2;
println!("{}", s3); // hello world
最后一种是使用format!
宏:
let s1 = String::from("hello ");
let s2 = String::from("world");
let s3 = format!("{}{}", s1, s2);
println!("{}", s3); // hello world
字符串索引,我们下面先看一个例子:
let s1 = String::from("我爱祖国");
println!("{}", s1[1]); // error[E0277]: the type `String` cannot be indexed by `{integer}`
上面报错的原因是因为,UTF-8
是不定长编码,但是String
的实现是基于 Vec
的封装的,数组中每一个元素都是一个字节,但UTF-8
中每一个汉字(或字符)都可能由一到四个字节组成。接着我们看一下字符串分隔操作:
let s1 = String::from("hello world");
println!("{}", &s1[0..3]); // hel
最后看一下字符串遍历操作:
let s1 = String::from("hello world");
for char in s1.chars() {
println!("{}", char)
}
更多的操作可以标准库文档:https://www.rustwiki.org.cn/zh-CN/std/string/struct.String.html
方法 | 描述 |
---|---|
new() | 创建一个新的字符串对象 |
to_string() | 将字符串字面量转换为字符串对象 |
replace() | 搜索并替换 |
as_str() | 提取包含整个 String 的字符串切片 |
push() | 将给定的 char 追加到该 String 的末尾 |
push_str() | 将给定的字符串切片追加到这个 String 的末尾 |
pop() | 从字符串缓冲区中删除最后一个字符并返回它 |
remove() | 从该 String 的字节位置删除 char 并将其返回 |
insert() | 在此 String 的字节位置插入一个字符 |
len() | 返回此 String 的长度,以字节为单位 |
is_empty() | 如果此 String 的长度为零,则返回 true ,否则返回 false |
clear() | 截断此 String ,删除所有内容 |
哈希映射,存储了K
类型键到V
类型值之间的映射关系。这个我们在Java、go里面也有这种数据类型。
具体操作可以看标准库文档:https://www.rustwiki.org.cn/zh-CN/std/collections/struct.HashMap.html
下面演示使用new
创建了一个空的HashMap
,使用insert
向HashMap
中添加元素。
let mut map = HashMap::new();
// let mut map = HashMap::with_capacity(10); // 设置指定容量的HashMap
map.insert(String::from("Blue"), 10);
map.insert(String::from("Red"), 100);
Rust
的HashMap
还提供form/into
方法可以用来创建存在初始值的HashMap
:
let map1 = HashMap::from([
("a", 1),
("b", 2),
("c", 3),
]);
// 使用into方法需要指定类型,此时编译器会推导出map2的类型是HashMap<_: &str, _: i32>
let map2: HashMap<_, _> = [("a", 1), ("b", 2), ("c", 3), ].into();
另外也可以通过entry
函数进行插入:
let mut map = HashMap::new();
let text = "hello world rust";
for ch in text.chars(){
// 判断字符是否存在,如果不存在则将初始化数量为0, 此时counter的类型是&mut i32
let counter = map.entry(ch).or_insert(0);
// 存在的话增加数量 *counter是解引用
*counter += 1;
}
println!("{:?}", map);
Rust中哈希映射提供了多种方法访问哈希映射的键和值:
let mut map = HashMap::from([("a", 1), ("b", 2), ("c", 3)]);
// 访问key
map.keys().for_each(|key| {
println!("{}", key)
});
// 访问value
map.values().for_each(|value| {
println!("{}", value)
});
获取value
的方法也可以通过get
等方法,这些方法返回的都是Option
类型
map.get("a").map(|data| { println!("{}", data) });
map.get_mut("a").map(|data| { *data = 10 }); // get_mut可以获取可变引用
map.get_key_value("a").map(|(key, value)| {
println!("key: {}, value: {}", key.deref(), value)
}); // get_key_value 返回与提供的键相对应的键值
接着我们看一个删除/过滤的操作,
let mut map = HashMap::new();
let text = "hello world rust";
for ch in text.chars(){
let counter = map.entry(ch).or_insert(0);
*counter += 1;
}
// 删除前 => {'h': 1, 'l': 3, 'r': 2, 'o': 2, ' ': 2, 'd': 1, 't': 1, 'u': 1, 's': 1, 'e': 1, 'w': 1}
println!("删除前 => {:?}", map);
map.retain(|&k, v| { k != ' ' && *v > 1 });
// 删除后 => {'l': 3, 'r': 2, 'o': 2}
println!("删除后 => {:?}", map);
方法 | 描述 |
---|---|
capacity() | 获取容量 |
clear() | 清空 |
contains_key() | 是否包含key |
drain() | 清除map ,将所有键值对作为迭代器返回 |
drain_filter() | |
entry() | 在map 中获取给定键的对应项,以进行就地操作。 |
get/get_mut/get_key_value | 通过某一个key 获取value |
insert() | 插入 |
into_keys() | 创建一个消费迭代器,以任意顺序访问所有键。 调用后不能使用map |
into_values() | 创建一个消费迭代器,以任意顺序访问所有值。 调用后不能使用map |
is_empty() | 是否为空 |
keys() | 获取所有的key |
len | 获取长度 |
remove() | 根据某一个key 删除 |
remove_entry() | 从map 中删除一个键,如果该键以前在map 中,则返回存储的键和值 |
retain() | 删除指定谓词的元素,可以看着条件过滤 |