常见集合

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有一定的细节,其返回一个可以matchOption<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:?}");