Vector
创建 Vector
Vec::new函数1
2
3fn main() {
let v: Vec<i32> = Vec::new();
}使用初始值创建
Vec<T>,使用vec!宏1
2
3fn main() {
let v = vec![1, 2, 3];
}更新 Vector
向 Vector 添加元素,使用 push 方法
1 | fn main() { |
删除 Vector
- 与任何其它 struct 一样,当 Vector 离开作用域后
- 它就被清理掉了
- 它所有元素也被清理掉了
读取 Vector 的元素
- 两种方式可以引用 Vector 里的值
- 索引
- get 方法
1 | fn main() { |
遍历 Vector 中的值
- for 循环
1 | fn main() { |
1 | fn main() { |
Vector + Enum 例子
使用 enum 来存储多种数据类型
- Enum 的变体可以附加不同类型的数据
- Enum 的变体定义在同一个 enum 类型下
1 | enum SpreadsheetCell { |
String
Rust 开发者经常会被字符串困扰的原因
- Rust 倾向于暴露可能的错误
- 字符串数据结构复杂
- UTF-8
字符串是什么
- Byte 的集合
- 提供了一些方法
- 将 byte 解析成文本
- Rust 的核心语言层面,只有一个字符串类型,字符串切片 str(或&str)
- 字符串切片:对存储在其他地方、UTF-8 编码的字符串的引用
- 字符串字面值:存储在二进制文件中,也是字符串切片
- String 类型
- 来自标准库,而不是核心语言
- 可增长、可修改、可拥有所有权
- 采用 UTF-8 编码
通常说的字符串是指
- String 和 &str
- 标准库里用的多
- UTF-8 编码
其它类型的字符串
- Rust 的标准库还包含了其它的字符串类型,例如:OsString、OsStr、CString、CStr
- String vs Str 后缀:拥有或借用的变体
- 可存储不同编码的文本或在内存中以不同的形式展现
- Libary crate (第三方库)针对存储字符串可提供更多的选项
创建一个新的字符串(String)
很多
Vec<T>的操作都可用于 StringString::new函数1
2
3fn main() {
let mut s = String::new();
}使用初始值来创建 String
to_string()方法,可用于实现 Display trait 的类型,包括字符串字面值1
2
3
4
5
6fn main() {
let data = "initial contents";
let s = data.to_string();
let s1 = "initial contents".to_string();
}String::from函数,从字面值创建 String1
2
3fn main() {
let s = String::from("initial contents");
}
更新 String
push_str()方法:把一个字符串切片附加到 String1
2
3
4
5fn main() {
let mut s = String::from("foo");
s.push_str("bar");
println!("{}", a);
}push()方法:把单个字符附加到 String1
2
3
4fn main() {
let mut s = String::from("lo");
s.push('l');
}+:连接字符串使用了类似这个签名的方法
fn add(self, s: &str) -> String{...}- 标准库中的 add 方法使用了泛型
- 只能把 &str 添加到 String
- 解引用强制转换(deref coeracion)
1
2
3
4
5
6
7
8
9
10
11
12fn main() {
let s1 = String::from("hello, ");
let s2 = String::from("world");
// +前面是String,后面是引用,不能将两个String添加
let s3 = s1 + &s2;
println!("{}", s3);
// 因为拼接字符串时采用String,所以s1不可用
// println!("{}", s1);
println!("{}", s2);
}
format!:链接多个字符串按索引语法访问 String 的某部分,会报错
1
2
3
4
5fn main() {
let s = String::from("tic");
// 报错
let s1 = s[0];
}Rust 的字符串不支持索引语法访问
String 内部表示
- String 是对
Vec<u8>的包装len()方法返回 String 的字符数
String 的字符索引并不一定能对上有效的字符
1 | fn main() { |
字节、标量值、字形簇
Bytes,Scalar Values,Grapheme Clusters
- Rust 有三种看待字符串的方式
- 字节
- 标量值
- 字形簇(最接近所谓的 “字母”)
- Rust 不允许对 String 进行索引的最后一个原因
- 索引操作应消耗一个常量时间
O(1) - 而 String 无法保证:需要遍历所有内容,来确定有多少个合法的字符
- 索引操作应消耗一个常量时间
切割 String
- 可以使用
[]和一个范围来创建字符串的切片- 必须谨慎使用
- 如果切割时跨越了字符边界,程序就会 panic
1 | fn main() { |
遍历 String 的方法
- 对于标量值:
chars()方法遍历 - 对于字节:
bytes()方法 - 对于字形簇:很复杂,标准库未提供
String 不简单
- Rust 选择将正确处理 String 数据作为所有 Rust 程序的默认行为
- 程序员必须在处理 UTF-8 数据之前投入更多的精力
- 可防止在开发后期处理涉及非 ASCII 字符的错误
HashMap<K, V>
- 键值对的形式存储数据,一个键(key)对应一个值(Value)
- Hash 函数:决定如何在内存中存储 K 和 V
- 适用场景:通过 K(任何类型)来寻找数据,而不是通过索引
- HashMap 用的较少,不在 Prelude 中
- 标准库对其支持较少,没有内置的宏来创建 HashMap
- 数据存储在 heap 上
- 同构的,一个 HashMap 中
- 所有的 K 必须时同一种类型
- 所有的 V 必须时同一种类型
创建 HashMap
- 创建空 HashMap:
new()函数 - 添加数据:
insert()方法
1 | use std::collections::HashMap; |
collect 方法创建 HashMap
- 在元素类型为 Tuple 的 Vector 上使用 collect 方法,可以组建一个 HashMap
- 要求 Tuple 有两个值:一个作为 K,一个作为 V
- collect 方法可以把数据整合成很多种集合类型,包括 HashMap
- 返回值需要显式指定类型
1 | use std::collections::HashMap; |
HashMap 和所有权
对于实现了 Copy trait 的类型(例如 i32),值会被复制到 HashMap 中
对于拥有所有权的值(例如 String),值会被移动,所有权会转移给 HashMap
1
2
3
4
5
6
7
8
9
10
11
12use std::collections::HashMap;
fn main() {
let field_name = String::from("Favorite color");
let field_value = String::from("Blue");
let mut map = HashMap::new();
map.insert(field_name, field_value);
// 会报错,因为 field_name 与 field_value 所有权交给了 HashMap
println!("{}, {}", field_name, field_value);
}如果将值的引用插入到 HashMap ,值本身不会移动
- 在 HashMap 有效的期间,被引用的值必须保持有效
1
2
3
4
5
6
7
8
9
10
11use std::collections::HashMap;
fn main() {
let field_name = String::from("Favorite color");
let field_value = String::from("Blue");
let mut map = HashMap::new();
map.insert(&field_name, &field_value);
println!("{}, {}", field_name, field_value);
}
访问 HashMap 中的值
- get 方法
- 参数:K
- 返回:Option<&V>
1 | use std::collections::HashMap; |
遍历 HashMap
- for 循环
1 | use std::collections::HashMap; |
更新 HashMap
- HashMap 大小可变
- 每个 K 同时只能对应一个 V
- 更新 HashMap 中的数据
- K 已经存在,对应一个 V
- 替换现有的 V
- 保留现有的 V,忽略新的 V
- 合并现有的 V 和新的 V
- K 不存在
- 添加一对新的 KV
- K 已经存在,对应一个 V
替换现有的 V
如果向 HashMap 插入一对 KV,然后再插入同样的 K,但是不同的 V,那么原来的 V 会被替换掉
1 | use std::collections::HashMap; |
K不对应任何值,才插入 V
- entry 方法:检查指定的 K 是否对应一个 V
- 参数为 K
- 返回 enum Entry:代表值是否存在
- Entry 的 or_insert() 方法
- result:
- 如果 K 存在,返回对应 V 的一个可变引用
- 如果 K 不存在,将方法参数作为 K 的新值插进去,返回这个值的可变引用
- result:
1 | use std::collections::HashMap; |
基于现有V来更新V
1 | use std::collections::HashMap; |
Hash 函数
- 默认情况下,HashMap 使用加密功能强大的 Hash 函数,可以抵抗拒绝服务(Dos)攻击
- 不是可用的最快的 Hash 算法
- 但具有更好安全性
- 可以指定不同的 hasher 来切换成另一个函数
- hasher 是实现 BuildHasher trait 的类型