From 49be9987db6064c68578750fd44a899047d065e0 Mon Sep 17 00:00:00 2001 From: YangFong <70502828+YangFong@users.noreply.github.com> Date: Sat, 5 Mar 2022 18:34:39 +0800 Subject: [PATCH] docs: optimize the Collections chapter format --- src/basic/collections/hashmap.md | 22 +++++++++++----------- src/basic/collections/vector.md | 13 ++++++++----- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/basic/collections/hashmap.md b/src/basic/collections/hashmap.md index 639e6152..3aace70f 100644 --- a/src/basic/collections/hashmap.md +++ b/src/basic/collections/hashmap.md @@ -1,12 +1,12 @@ # KV 存储 HashMap -和动态数组一样,`HashMap` 也是 Rust 标准库中提供的集合类型,但是又与动态数组不同,`HashMap` 中存储的是一一映射的 `KV `键值对,并提供了平均复杂度为 `O(1)` 的查询方法,当我们希望通过一个 `Key` 去查询值时,该类型非常有用,以致于 Go 语言将该类型设置成了语言级别的内置特性。 +和动态数组一样,`HashMap` 也是 Rust 标准库中提供的集合类型,但是又与动态数组不同,`HashMap` 中存储的是一一映射的 `KV` 键值对,并提供了平均复杂度为 `O(1)` 的查询方法,当我们希望通过一个 `Key` 去查询值时,该类型非常有用,以致于 Go 语言将该类型设置成了语言级别的内置特性。 Rust 中哈希类型(哈希映射)为 `HashMap`,在其它语言中,也有类似的数据结构,例如 `hash map`,`map`,`object`,`hash table`,`字典` 等等,引用小品演员孙涛的一句台词:大家都是本地狐狸,别搁那装貂 :)。 ## 创建 HashMap -跟创建动态数组 `Vec` 的方法类似,可以使用 `new` 方法来创建` HashMap`,然后通过` insert` 方法插入键值对。 +跟创建动态数组 `Vec` 的方法类似,可以使用 `new` 方法来创建 `HashMap`,然后通过 `insert` 方法插入键值对。 #### 使用 new 方法创建 @@ -24,7 +24,7 @@ my_gems.insert("河边捡的误以为是宝石的破石头", 18); 很简单对吧?跟其它语言没有区别,聪明的同学甚至能够猜到该 `HashMap` 的类型:`HashMap<&str,i32>`。 -但是还有一点,你可能没有注意,那就是使用 `HashMap` 需要手动通过 `use ...` 从标准库中引入到我们当前的作用域中来,仔细回忆下,之前使用另外两个集合类型 `String` 和` Vec` 时,我们是否有手动引用过?答案是 `No`,因为 `HashMap` 并没有包含在 Rust 的 [`prelude`](../../appendix/prelude.md) 中(Rust 为了简化用户使用,提前将最常用的类型自动引入到作用域中)。 +但是还有一点,你可能没有注意,那就是使用 `HashMap` 需要手动通过 `use ...` 从标准库中引入到我们当前的作用域中来,仔细回忆下,之前使用另外两个集合类型 `String` 和 `Vec` 时,我们是否有手动引用过?答案是 `No`,因为 `HashMap` 并没有包含在 Rust 的 [`prelude`](../../appendix/prelude.md) 中(Rust 为了简化用户使用,提前将最常用的类型自动引入到作用域中)。 所有的集合类型都是动态的,意味着它们没有固定的内存大小,因此它们底层的数据都存储在内存堆上,然后通过一个存储在栈中的引用类型来访问。同时,跟其它集合类型一致,`HashMap` 也是内聚性的,即所有的 `K` 必须拥有同样的类型,`V` 也是如此。 @@ -46,8 +46,8 @@ fn main() { let teams_list = vec![ ("中国队".to_string(), 100), - ("美国队".to_string(),10), - ("日本队".to_string(),50), + ("美国队".to_string(), 10), + ("日本队".to_string(), 50), ]; let mut teams_map = HashMap::new(); @@ -69,8 +69,8 @@ fn main() { let teams_list = vec![ ("中国队".to_string(), 100), - ("美国队".to_string(),10), - ("日本队".to_string(),50), + ("美国队".to_string(), 10), + ("日本队".to_string(), 50), ]; let teams_map: HashMap<_,_> = teams_list.into_iter().collect(); @@ -111,7 +111,7 @@ fn main() { handsome_boys.insert(name, age); println!("因为过于无耻,{}已经被从帅气男孩名单中除名", name); - println!("还有,他的真实年龄远远不止{}岁",age); + println!("还有,他的真实年龄远远不止{}岁", age); } ``` @@ -147,7 +147,7 @@ fn main() { std::mem::drop(name); println!("因为过于无耻,{:?}已经被除名", handsome_boys); - println!("还有,他的真实年龄远远不止{}岁",age); + println!("还有,他的真实年龄远远不止{}岁", age); } ``` @@ -183,7 +183,7 @@ let score: Option<&i32> = scores.get(&team_name); 上面有几点需要注意: -- `get` 方法返回一个 `Option<&i32> `类型:当查询不到时,会返回一个 `None`,查询到时返回 `Some(&i32)` +- `get` 方法返回一个 `Option<&i32>` 类型:当查询不到时,会返回一个 `None`,查询到时返回 `Some(&i32)` - `&i32` 是对 `HashMap` 中值的借用,如果不使用借用,可能会发生所有权的转移 还可以通过循环的方式依次遍历 `KV` 对: @@ -297,6 +297,6 @@ hash.insert(42, "the answer"); assert_eq!(hash.get(&42), Some(&"the answer")); ``` -> 目前,`HashMap` 使用的哈希函数是 `SipHash`,它的性能不是很高,但是安全性很高。`SipHash` 在中等大小的 `Key` 上,性能相当不错,但是对于小型的 `Key` (例如整数)或者大型 `Key` (例如字符串)来说,性能还是不够好。若你需要极致性能,例如实现算法,可以考虑这个库:[ahash](https://github.com/tkaitchuck/ahash) +> 目前,`HashMap` 使用的哈希函数是 `SipHash`,它的性能不是很高,但是安全性很高。`SipHash` 在中等大小的 `Key` 上,性能相当不错,但是对于小型的 `Key` (例如整数)或者大型 `Key` (例如字符串)来说,性能还是不够好。若你需要极致性能,例如实现算法,可以考虑这个库:[ahash](https://github.com/tkaitchuck/ahash) 最后,如果你想要了解 `HashMap` 更多的用法,请参见本书的标准库解析章节:[HashMap 常用方法](../../std/hashmap.md) diff --git a/src/basic/collections/vector.md b/src/basic/collections/vector.md index b7d904d9..a1090c0c 100644 --- a/src/basic/collections/vector.md +++ b/src/basic/collections/vector.md @@ -1,6 +1,6 @@ # 动态数组 Vector -动态数组类型用`Vec`表示,事实上,在之前的章节,它的身影多次出现,我们一直没有细讲,只是简单的把它当作数组处理。 +动态数组类型用 `Vec` 表示,事实上,在之前的章节,它的身影多次出现,我们一直没有细讲,只是简单的把它当作数组处理。 动态数组允许你存储多个值,这些值在内存中一个紧挨着另一个排列,因此访问其中某个元素的成本非常低。动态数组只能存储相同类型的元素,如果你想存储不同类型的元素,可以使用之前讲过的枚举类型或者特征对象。 @@ -18,7 +18,7 @@ let v: Vec = Vec::new(); ``` -这里,`v` 被显式地声明了类型`Vec`,这是因为 Rust 编译器无法从 `Vec::new()` 中得到任何关于类型的暗示信息,因此也无法推导出 `v` 的具体类型,但是当你向里面增加一个元素后,一切又不同了: +这里,`v` 被显式地声明了类型 `Vec`,这是因为 Rust 编译器无法从 `Vec::new()` 中得到任何关于类型的暗示信息,因此也无法推导出 `v` 的具体类型,但是当你向里面增加一个元素后,一切又不同了: ```rust let mut v = Vec::new(); @@ -66,7 +66,10 @@ v.push(1); ## 从 Vector 中读取元素 -读取指定位置的元素有两种方式可选:通过下标索引访问或者使用 `get` 方法: +读取指定位置的元素有两种方式可选: + +- 通过下标索引访问。 +- 使用 `get` 方法。 ```rust let v = vec![1, 2, 3, 4, 5]; @@ -142,7 +145,7 @@ error: could not compile `collections` due to previous error 其实想想,**在长大之后,我们感激人生路上遇到过的严师益友,正是因为他们,我们才在正确的道路上不断前行,虽然在那个时候,并不能理解他们**,而 Rust 就如那个良师益友,它不断的在纠正我们不好的编程习惯,直到某一天,你发现自己能写出一次性通过的漂亮代码时,就能明白它的良苦用心。 -> 若读者想要更深入的了解`Vec`,可以看看[Rustonomicon](https://nomicon.purewhite.io/vec/vec.html),其中从零手撸一个动态数组,非常适合深入学习 +> 若读者想要更深入的了解 `Vec`,可以看看[Rustonomicon](https://nomicon.purewhite.io/vec/vec.html),其中从零手撸一个动态数组,非常适合深入学习 ## 迭代遍历 Vector 中的元素 @@ -228,4 +231,4 @@ fn main() { 在实际使用场景中,特征对象数组要比枚举数组常见很多,主要原因在于[特征对象](../trait/trait-object.md)非常灵活,而编译器对枚举的限制较多,且无法动态增加类型。 -最后,如果你想要了解 `Vector `更多的用法,请参见本书的标准库解析章节:[`Vector`常用方法](../../std/vector.md) +最后,如果你想要了解 `Vector` 更多的用法,请参见本书的标准库解析章节:[`Vector`常用方法](../../std/vector.md)