Learning Rust - 5 常见集合
常见集合
1 Vector
vector
允许在一个单独的数据结构中储存多于一个的值,它在内存中彼此相邻地排列所有的值。
vector
只能储存相同类型的值。
创建 vector
例1.1:// 创建一个vector
let v: Vec<i32> = Vec::new();
let v = vec![1, 2, 3]; // 使用 vec! 宏
更新 vector
例1.2:let mut v = Vec::new(); // 注意可变性
v.push(5);
v.push(6);
v.push(7);
v.push(8);
读取 vector
例1.3:let v = vec![1, 2, 3, 4, 5];
let third: &i32 = &v[2]; // 使用索引
println!("The third element is {third}");
let third: Option<&i32> = v.get(2); // 使用 get 方法
match third {
Some(third) => println!("The third element is {third}"),
None => println!("There is no third element."),
}
关于使用get
有一定的细节,其返回一个可以match
的Option<T>
,并且对于溢出的索引不会报错而是会返回None
。
使用枚举来存储多种类型
前面有提到,vector
只能存储相同的类型,但是同一枚举的不同成员被定义为相同的类型,因此可以实现多种类型的存储。
例1.4:enum SpreadsheetCell {
Int(i32),
Float(f64),
Text(String),
}
let row = vec![
SpreadsheetCell::Int(3),
SpreadsheetCell::Text(String::from("blue")),
SpreadsheetCell::Float(10.12),
];
2 字符串
新建字符串
例2.1:let mut s = String::new();
s.push_str("initial contents"); // push方法不获取所有权
let data = "initial contents";
let s = data.to_string();
// 该方法也可直接用于字符串字面值:
let s = "initial contents".to_string();
let s = String::from("initial contents");
字符串连接
例2.2:let s1 = String::from("Hello, ");
let s2 = String::from("world!");
let s3 = s1 + &s2; // 注意 s1 被移动了,不能继续使用
+
运算符使用了 add 函数,这个函数签名看起来像这样:
fn add(self, s: &str) -> String { ... } |
有一个问题,函数中的参数类型是&str
,而我们提供的是&String
类型,之所以能够在add
调用中使用&s2
是因为&String
可以被强转(coerced)
成&str
。当add
函数被调用时,Rust
使用了一个被称为Deref强制转换(deref coercion)
的技术,可以将其理解为它把&s2
变成了&s2[..]
。
使用format!
宏简化操作:
例2.3:let s1 = String::from("tic");
let s2 = String::from("tac");
let s3 = String::from("toe");
let s = format!("{s1}-{s2}-{s3}"); // tic-tac-toe
索引字符串
Rust
不允许使用索引语法访问字符串的单个字符,因为考虑到不同编码单个字符所需要使用的字节数不同(详细的参考手册链接)。
遍历字符串
明确指出使用字符还是字节:
例2.4:for c in "Зд".chars() {
println!("{c}");
}
// З
// д
for b in "Зд".bytes() {
println!("{b}");
}
// 208
// 151
// 208
// 180
3 HashMap
创建一个 HashMap
HashMap<K, V>
类型储存了一个键类型K
对应一个值类型V
的映射。它通过一个哈希函数(hashing function)
来实现映射,决定如何将键和值放入内存中。
例3.1:use std::collections::HashMap;
fn main(){
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
}
访问 HashMap 中的元素
例3.2:use std::collections::HashMap;
fn main(){
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).copied().unwrap_or(0);
// .copied() 方法可以将 Option<&T> 转换为 Option<T>
println!("{score}");
}
遍历访问 HashMap 中的元素
例3.3:...
for (key, value) in &scores {
println!("{key}: {value}");
}
覆盖一个值
例3.4:use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Blue"), 25);
println!("{scores:?}");
只在键没有对应值时插入键值对
例3.5: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:?}");
根据旧值更新一个值
例3.6: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:?}");