8.1-2 Vector
Vector是堆内存的数据结构,使用Vec<T>
定义 (类似C++的STL中的vector
)
- 由标准库提供
- 可存储多个值
- 只能存储相同类型的数据
- 值在内存中连续存放
初始化
例子1
1 | let v: Vec<i32> = Vec::new(); |
事实上,这里不指定v
的类型也可以,当Vector插入第一个元素的时候,编译器会根据该元素推断
例子2
在使用一些指定初始值创建Vector时,可以使用vec!
这个宏,如
1 | let v = vec![1, 2, 3]; |
Push元素
与C++的std::vector
类似:
1 | v.push(1); |
删除Vector
与struct等类似,当其离开作用域,将会连同内部元素被一同清理
读取Vector的元素
有两种方式可以读取Vector的元素,索引和get
方法,例如
1 | let v: Vec<i32> = vec![1, 2, 3]; |
在处理越界问题时,
- 索引会使程序panic并终止
get
则返回None
这方便我们根据需求选择读取方式
所有权和借用规则
前面提到过的规则对于Vector也适用,例如
1 | fn main() { |
会报错,因为不能同时出现可变借用和不可变借用
- 其中
push(&mut self, value: T)
函数是定义在可变借用上
在底层原理上,这么设计的原因是:
- 假如现在需要向某个Vector添加元素,但是原始的内存块长度不够了,需要在另一个地方重新寻找内存块来创建新Vector,这就会导致不可变借用依然指向旧的地址,从而出现错误
遍历Vector
对于不可变引用:
1 | fn main() { |
对于可变引用(例如修改数组元素):
1 | fn main() { |
- 其中
*
是解引用运算符
Vector存储“多种数据”
前面说过,Vector不能直接存储多种数据,但是可以借助enum来实现,例如
1 | enum SpreadsheetCell { |
8.3-4 String
字符串本质上是Byte的集合,并提供一些方法将Byte解析成文本
在Rust的核心语言层面,只有一个字符串类型:
- 字符串切片类型
str
或&str
所谓的String
类型则来自标准库而非核心语言
- 可增长、可修改、可拥有
在Rust中常说的字符串指的是String
和&str
,它们都是UTF8编码
此外,Rust标准库还提供了其它的字符串类型,例如OsString, OsStr, CString, CStr
等等
- 一般以
String
结尾的字符串是可获得所有权的 - 一般以
Str
结尾的字符串是可借用的
创建一个字符串 (String)
创建空字符串:
1 | let mut s = String::new(); |
从字面值创建一个字符串:
1 | let data = "initial contents"; |
- 其中
to_string()
方法可以用于实现了Display trait的类型,包括字符串字面值
更新String
可以使用push_str()
方法来拼接
1 | let mut s = String::from("foo"); |
其中push_str(&mut self, string: &str)
不会获得字符串的所有权,例如我们也可以
1 | let mut s = String::from("foo"); |
如果想增加增添字符,可以适用push()
,例如
1 | let mut s = String::from("lo"); |
拼接的加法运算符
对于String,使用+
连接字符串,会使用类似fn add(self, s: &str) -> String {...}
的方法,例如
1 | let s1 = String::from("hello"); |
- 其中
&s2
是对String
类型的引用,而s1
在拼接操作之后将不可用,因为add函数会取得第一个参数的所有权 - 此外,虽然
&s2
不是&str
类型,但标准库的add方法使用了泛型,对其进行了解引用强制转换(deref coercion)
拼接的宏
使用format!
这个宏,可以连接多个字符串,例如
1 | let s1 = String::from("tic"); |
可以改写为
1 | let s = format!("{}-{}-{}", s1, s2, s3); |
- 相比
+
,format!
不会取得参数的所有权,因此这些参数之后可以继续使用
对String以索引形式访问
如果类似Vector那样访问String元素,例如
1 | let s1 = String::from("hello"); |
则会报错,因为String并没有实现index索引这个trait!
- 这是因为String的底层是
Vec<u8>
的包装,而对于不同的语言,每个字符会对应不同的长度,从而引起问题,因此Rust便在设计上杜绝了这种使用方式
Bytes, Scalar values, Grapheme clusters
从字节角度来看待字符串:
1 | let w = "東京駅"; |
输出为
1 | 230 |
从标量值来看待字符串:
1 | let w = "東京駅"; |
输出为
1 | 東 |
对于最后一种Grapheme clusters的方法,需要借助第三方crate来实现,这里省略
- 还有一个Rust不允许String进行索引的原因是
- 索引操作应当消耗O(1)时间
- 但String需要遍历所有内容来查看有多少合法字符,因此无法保证O(1)
切片String
不同语言的字符所占的字节不同,例如每个汉字占3个字节,因此
1 | let w = "東京駅"; |
将会拿到東
这个字,若少于3个字节则会报错
对于英文,则没有这个问题:
1 | let w = "abcdefg"; |
因此,在切割字符串时,必须注意字符边界,否则程序会panick
8.5 HashMap
即哈希查找或哈希表,对于HashMap<K, V>
,
K
为key,即键V
为value,即值
本部分暂略,回头补