diff --git a/.idea/advanced_rust.iml b/.idea/advanced_rust.iml
index dda0920..c2bc827 100644
--- a/.idea/advanced_rust.iml
+++ b/.idea/advanced_rust.iml
@@ -14,6 +14,9 @@
+
+
+
diff --git a/Cargo.lock b/Cargo.lock
index d197f98..ef0f8d6 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -75,6 +75,21 @@ dependencies = [
name = "chapter08-vec"
version = "0.1.0"
+[[package]]
+name = "chapter09-hashmap"
+version = "0.1.0"
+
+[[package]]
+name = "chapter10-error"
+version = "0.1.0"
+dependencies = [
+ "thiserror",
+]
+
+[[package]]
+name = "chapter11-closure"
+version = "0.1.0"
+
[[package]]
name = "crossbeam-deque"
version = "0.8.5"
@@ -359,6 +374,26 @@ dependencies = [
"unicode-ident",
]
+[[package]]
+name = "thiserror"
+version = "1.0.63"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.63"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "thread_local"
version = "1.1.8"
diff --git a/Cargo.toml b/Cargo.toml
index faa48c7..da630c0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,7 +1,8 @@
-
-
[workspace]
-members = [ "app", "chapter01-log", "chapter02-feature", "chapter03-ownership", "chapter04-lifecycle", "chapter05-memory", "chapter06-type", "chapter07-pointer", "chapter08-vec"]
+members = ["app", "chapter01-log",
+ "chapter02-feature", "chapter03-ownership",
+ "chapter04-lifecycle", "chapter05-memory", "chapter06-type",
+ "chapter07-pointer", "chapter08-vec", "chapter09-hash", "chapter10-error", "chapter11-closure"]
[workspace.dependencies]
diff --git a/README.md b/README.md
index ed46d44..e33373e 100644
--- a/README.md
+++ b/README.md
@@ -69,6 +69,20 @@
- [2 动态数组 Vector 和 数组 [T; n] 如何转化成 &[T]](chapter08-vec/src/vec2-slice.rs)
- [3 String、&String 转换成 &str](chapter08-vec/src/vec3-string-slice.rs)
+## [第九章:hash](chapter09-hash/hashmap.md)
+
+- 1 hashmap cap 扩容和缩容
+- 2 HashSet
+- 3 BTreeMap
+
+## [第十章:error 错误处理](chapter10-error/error.md)
+
+- [1 使用 ? 进行传播错误](chapter10-error/src/error1-transfer.rs)
+
+## [第十一章:闭包](chapter11-closure/closure.md)
+
+- 1 闭包的大小跟参数、局部变量都无关,只跟捕获的变量有关
+
## 参考
- [Rust语言圣经](https://github.com/sunface/rust-course)
diff --git a/chapter09-hash/Cargo.toml b/chapter09-hash/Cargo.toml
new file mode 100644
index 0000000..0282939
--- /dev/null
+++ b/chapter09-hash/Cargo.toml
@@ -0,0 +1,19 @@
+[package]
+name = "chapter09-hashmap"
+version.workspace = true
+edition.workspace = true
+authors.workspace = true
+
+[[bin]]
+name = "hash1"
+path = "src/hash1-cap.rs"
+
+
+[[bin]]
+name = "hash2"
+path = "src/hash2-hashset.rs"
+
+
+[[bin]]
+name = "hash3"
+path = "src/hash3-btreemap.rs"
\ No newline at end of file
diff --git a/chapter09-hash/hashmap.md b/chapter09-hash/hashmap.md
new file mode 100644
index 0000000..5cecd1b
--- /dev/null
+++ b/chapter09-hash/hashmap.md
@@ -0,0 +1,99 @@
+# hash
+
+## hashMap
+
+### 结构
+
+```rust
+pub struct HashMap {
+ pub(crate) hash_builder: S,
+ pub(crate) table: RawTable<(K, V), A>,
+}
+```
+
+```rust
+pub struct RawTable {
+ table: RawTableInner,
+ alloc: A,
+ // Tell dropck that we own instances of T.
+ marker: PhantomData,
+}
+
+/// Non-generic part of `RawTable` which allows functions to be instantiated only once regardless
+/// of how many different key-value types are used.
+struct RawTableInner {
+ // 哈希表中哈希桶的数量减一
+ bucket_mask: usize,
+
+ // [Padding], T1, T2, ..., Tlast, C1, C2, ...
+ // ^ points here
+ // 指向堆内存哈希表末端的 ctrl 区
+ ctrl: NonNull,
+
+ // Number of elements that can be inserted before we need to grow the table
+ growth_left: usize,
+
+ // Number of elements in the table, only really used by len()
+ items: usize,
+}
+```
+
+### 扩容和缩容
+
+当 HashMap::new() 时,它并没有分配空间,容量为零,随着哈希表不断插入数据,它会以 2 的幂减一的方式增长,最小是3。
+
+以 2 的 n 次幂的方式进行扩容,0,3 (22 - 1),7 (23 - 1),14 (24 - 24 * 12.5%),28 (25 - 25 * 12.5%)
+
+```rust
+pub struct HashMap {
+ base: base::HashMap,
+}
+
+impl HashMap
+where
+ K: Eq + Hash,
+ S: BuildHasher,
+{
+ pub fn insert(&mut self, k: K, v: V) -> Option {
+ self.base.insert(k, v)
+ }
+}
+```
+
+```rust
+impl HashMap
+where
+ K: Eq + Hash,
+ S: BuildHasher,
+ A: Allocator,
+{
+ pub fn insert(&mut self, k: K, v: V) -> Option {
+ // 哈希,得到一个哈希值 hash
+ let hash = make_hash::(&self.hash_builder, &k);
+ let hasher = make_hasher::<_, V, S>(&self.hash_builder);
+ match self
+ .table
+ .find_or_find_insert_slot(hash, equivalent_key(&k), hasher)
+ {
+ Ok(bucket) => Some(mem::replace(unsafe { &mut bucket.as_mut().1 }, v)),
+ Err(slot) => {
+ unsafe {
+ self.table.insert_in_slot(hash, slot, (k, v));
+ }
+ None
+ }
+ }
+ }
+}
+```
+
+当删除表中的数据时,原有的表大小不变,只有显式地调用 shrink_to_fit,才会让哈希表变小。
+
+## HashSet
+
+```rust
+pub struct HashSet {
+ pub(crate) map: HashMap,
+}
+
+```
\ No newline at end of file
diff --git a/chapter09-hash/src/hash1-cap.rs b/chapter09-hash/src/hash1-cap.rs
new file mode 100644
index 0000000..4cc1a57
--- /dev/null
+++ b/chapter09-hash/src/hash1-cap.rs
@@ -0,0 +1,33 @@
+use std::collections::HashMap;
+
+fn main() {
+ let mut map = HashMap::new();
+ explain("empty", &map);
+
+ map.insert("a", 1);
+ explain("added 1", &map);
+
+ map.insert("b", 2);
+ map.insert("c", 3);
+ explain("added 3", &map);
+
+ map.insert("d", 4);
+ explain("added 4", &map);
+
+ // assert_eq!(map.get("a"), Some(1).as_ref());
+ assert_eq!(map.get(&"a"), Some(&1));
+ assert_eq!(map.get_key_value(&"b"), Some((&"b", &2)));
+
+ map.remove(&"a"); // 删除后就找不到了
+ assert_eq!(map.contains_key(&"a"), false);
+ assert_eq!(map.get(&"a"), None);
+ explain("removed", &map);
+
+ // shrink 后哈希表变小
+ map.shrink_to_fit();
+ explain("shrinked", &map);
+}
+
+fn explain(name: &str, map: &HashMap) {
+ println!("{}:len:{},cap:{}", name, map.len(), map.capacity())
+}
diff --git a/chapter09-hash/src/hash2-hashset.rs b/chapter09-hash/src/hash2-hashset.rs
new file mode 100644
index 0000000..3a93bda
--- /dev/null
+++ b/chapter09-hash/src/hash2-hashset.rs
@@ -0,0 +1,38 @@
+use std::collections::HashSet;
+
+fn main() {
+ let mut a: HashSet = vec![1i32, 2, 3].into_iter().collect();
+ let mut b: HashSet = vec![2i32, 3, 4].into_iter().collect();
+
+ assert!(a.insert(4));
+ assert!(a.contains(&4));
+
+ // 如果值已经存在,那么 `HashSet::insert()` 返回 false。
+ assert!(b.insert(4), "Value 4 is already in set B!");
+ // 改正 ^ 将此行注释掉。
+
+ b.insert(5);
+
+ // 若一个集合(collection)的元素类型实现了 `Debug`,那么该集合也就实现了 `Debug`。
+ // 这通常将元素打印成这样的格式 `[elem1, elem2, ...]
+ println!("A: {:?}", a);
+ println!("B: {:?}", b);
+
+ // 乱序打印 [1, 2, 3, 4, 5]。
+ println!("Union: {:?}", a.union(&b).collect::>());
+
+ // 这将会打印出 [1]
+ println!("Difference: {:?}", a.difference(&b).collect::>());
+
+ // 乱序打印 [2, 3, 4]。
+ println!(
+ "Intersection: {:?}",
+ a.intersection(&b).collect::>()
+ );
+
+ // 打印 [1, 5]
+ println!(
+ "Symmetric Difference: {:?}",
+ a.symmetric_difference(&b).collect::>()
+ );
+}
diff --git a/chapter09-hash/src/hash3-btreemap.rs b/chapter09-hash/src/hash3-btreemap.rs
new file mode 100644
index 0000000..5cc1d59
--- /dev/null
+++ b/chapter09-hash/src/hash3-btreemap.rs
@@ -0,0 +1,31 @@
+use std::collections::BTreeMap;
+
+fn main() {
+ let map = BTreeMap::new();
+ let mut map = explain("empty", map);
+
+ for i in 0..16usize {
+ map.insert(format!("Danny {}", i), i);
+ }
+
+ let mut map = explain("added", map);
+
+ map.remove("Danny 1");
+
+ let map = explain("remove 1", map);
+
+ for item in map.iter() {
+ println!("{:?}", item);
+ }
+}
+
+// BTreeMap 结构有 height,node 和 length
+// 我们 transmute 打印之后,再 transmute 回去
+fn explain(name: &str, map: BTreeMap) -> BTreeMap {
+ let arr: [usize; 3] = unsafe { std::mem::transmute(map) };
+ println!(
+ "{}: height: {}, root node: 0x{:x}, len: 0x{:x}",
+ name, arr[0], arr[1], arr[2]
+ );
+ unsafe { std::mem::transmute(arr) }
+}
diff --git a/chapter10-error/Cargo.toml b/chapter10-error/Cargo.toml
new file mode 100644
index 0000000..4ffcf22
--- /dev/null
+++ b/chapter10-error/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "chapter10-error"
+version.workspace = true
+edition.workspace = true
+authors.workspace = true
+
+[dependencies]
+thiserror = "1.0"
+
+
+[[bin]]
+name = "error1"
+path = "src/error1-transfer.rs"
+
diff --git a/chapter10-error/error-situation.png b/chapter10-error/error-situation.png
new file mode 100644
index 0000000..319eac9
Binary files /dev/null and b/chapter10-error/error-situation.png differ
diff --git a/chapter10-error/error.md b/chapter10-error/error.md
new file mode 100644
index 0000000..733e21f
--- /dev/null
+++ b/chapter10-error/error.md
@@ -0,0 +1,153 @@
+# error
+
+![img.png](error-situation.png)
+
+Rust 偷师 Haskell,构建了对标 Maybe 的 Option 类型和 对标 Either 的 Result 类型。
+
+![img.png](option_n_result.png)
+
+## ? 操作符
+
+早期 Rust 提供了 try! 宏来简化错误的显式处理,后来为了进一步提升用户体验,try! 被进化成 ? 操作符
+
+如果你只想传播错误,不想就地处理,可以用 ? 操作符
+
+## Error trait 和错误类型的转换
+
+```rust
+// intellij-rust/stdlib-local-copy/1.78.0-ba6645f9113d6a36b36e041b03064c99e1ae9e85/library/core/src/error.rs
+pub trait Error: Debug + Display {
+ #[stable(feature = "error_source", since = "1.30.0")]
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ None
+ }
+
+ /// Gets the `TypeId` of `self`.
+ #[doc(hidden)]
+ #[unstable(
+ feature = "error_type_id",
+ reason = "this is memory-unsafe to override in user code",
+ issue = "60784"
+ )]
+ fn type_id(&self, _: private::Internal) -> TypeId
+ where
+ Self: 'static,
+ {
+ TypeId::of::()
+ }
+
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[deprecated(since = "1.42.0", note = "use the Display impl or to_string()")]
+ fn description(&self) -> &str {
+ "description() is deprecated; use Display"
+ }
+
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[deprecated(
+ since = "1.33.0",
+ note = "replaced by Error::source, which can support downcasting"
+ )]
+ #[allow(missing_docs)]
+ fn cause(&self) -> Option<&dyn Error> {
+ self.source()
+ }
+
+ /// ```
+ #[unstable(feature = "error_generic_member_access", issue = "99301")]
+ #[allow(unused_variables)]
+ fn provide<'a>(&'a self, request: &mut Request<'a>) {}
+}
+```
+
+## thiserror
+
+```rust
+use thiserror::Error;
+#[derive(Error, Debug)]
+#[non_exhaustive]
+pub enum DataStoreError {
+ #[error("data store disconnected")]
+ Disconnect(#[from] io::Error),
+ #[error("the data for key `{0}` is not available")]
+ Redaction(String),
+ #[error("invalid header (expected {expected:?}, found {found:?})")]
+ InvalidHeader {
+ expected: String,
+ found: String,
+ },
+ #[error("unknown data store error")]
+ Unknown,
+}
+```
+
+不使用 thiserror
+
+```rust
+use std::fmt;
+
+#[derive(Debug)]
+enum DataFetchError {
+ HttpError(u16),
+ Timeout,
+ InvalidPayload,
+}
+
+impl fmt::Display for DataFetchError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Self::HttpError(code) => write!(f, "HTTP error with code: {}", code),
+ Self::Timeout => write!(f, "Data fetching timed out"),
+ Self::InvalidPayload => write!(f, "Invalid payload received"),
+ }
+ }
+}
+
+impl std::error::Error for DataFetchError {}
+
+#[derive(Debug)]
+enum DatabaseError {
+ ConnectionFailed,
+ WriteFailed(String),
+}
+
+impl fmt::Display for DatabaseError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Self::ConnectionFailed => write!(f, "Failed to connect to database"),
+ Self::WriteFailed(reason) => write!(f, "Failed to write to database: {}", reason),
+ }
+ }
+}
+
+impl std::error::Error for DatabaseError {}
+```
+
+使用 thiserror
+
+```rust
+use thiserror::Error;
+
+#[derive(Debug, Error)]
+enum DataFetchError {
+ #[error("HTTP error with code: {0}")]
+ HttpError(u16),
+ #[error("Data fetching timed out")]
+ Timeout,
+ #[error("Invalid payload received")]
+ InvalidPayload,
+}
+
+#[derive(Debug, Error)]
+enum DatabaseError {
+ #[error("Failed to connect to database")]
+ ConnectionFailed,
+ #[error("Failed to write to database: {0}")]
+ WriteFailed(String),
+}
+
+```
+
+- 代码减少: 对于每种错误类型,我们都不再需要单独的Display和Error trait实现。这大大减少了样板代码,并提高了代码的可读性。
+- 错误消息与定义在一起: 使用thiserror,我们可以直接在错误定义旁边写出错误消息。这使得代码更加组织化,方便查找和修改。
+- 可维护性增加: 如果我们要添加或删除错误类型,只需要修改枚举定义并更新错误消息即可,而不需要在其他地方进行更改
+
diff --git a/chapter10-error/option_n_result.png b/chapter10-error/option_n_result.png
new file mode 100644
index 0000000..156e2ee
Binary files /dev/null and b/chapter10-error/option_n_result.png differ
diff --git a/chapter10-error/src/error1-transfer.rs b/chapter10-error/src/error1-transfer.rs
new file mode 100644
index 0000000..9cd041b
--- /dev/null
+++ b/chapter10-error/src/error1-transfer.rs
@@ -0,0 +1,13 @@
+use std::fs::File;
+use std::io::Read;
+
+fn read_file(name: &str) -> Result {
+ let mut f = File::open(name)?;
+ let mut contents = String::new();
+ f.read_to_string(&mut contents)?;
+ Ok(contents)
+}
+
+fn main() {
+ read_file("text1.txt").expect("file does not exist");
+}
diff --git a/chapter11-closure/Cargo.toml b/chapter11-closure/Cargo.toml
new file mode 100644
index 0000000..8aa7c20
--- /dev/null
+++ b/chapter11-closure/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+name = "chapter11-closure"
+version.workspace = true
+edition.workspace = true
+authors.workspace = true
+
+[dependencies]
+
+
+[[bin]]
+name = "closure1"
+path = "src/closure1-size.rs"
+
+
+[[bin]]
+name = "closure2"
+path = "src/closure2-fnonce.rs"
\ No newline at end of file
diff --git a/chapter11-closure/closure.md b/chapter11-closure/closure.md
new file mode 100644
index 0000000..467e56c
--- /dev/null
+++ b/chapter11-closure/closure.md
@@ -0,0 +1,46 @@
+# 闭包
+
+> A closure expression produces a closure value with a unique, anonymous type that cannot be written out.
+> A closure type is approximately equivalent to a struct which contains the captured variables.
+
+闭包是一种匿名类型,一旦声明,就会产生一个新的类型,但这个类型无法被其它地方使用。这个类型就像一个结构体,会包含所有捕获的变量。
+
+## 案例
+
+```rust
+#[stable(feature = "rust1", since = "1.0.0")]
+pub fn spawn(f: F) -> JoinHandle
+where
+ F: FnOnce() -> T,
+ F: Send + 'static,
+ T: Send + 'static,
+{
+ Builder::new().spawn(f).expect("failed to spawn thread")
+}
+```
+
+- F: FnOnce() → T,表明 F 是一个接受 0 个参数、返回 T 的闭包。
+- F: Send + 'static,说明闭包 F 这个数据结构,需要静态生命周期或者拥有所有权,并且它还能被发送给另一个线程。
+- T: Send + 'static,说明闭包 F 返回的数据结构 T,需要静态生命周期或者拥有所有权,并且它还能被发送给另一个线程。
+
+## Rust 的闭包类型
+
+### FnOnce 只能被调用一次
+
+```rust
+#[fundamental] // so that regex can rely that `&str: !FnMut`
+#[must_use = "closures are lazy and do nothing unless called"]
+// FIXME(effects) #[const_trait]
+pub trait FnOnce {
+ /// The returned type after the call operator is used.
+ #[lang = "fn_once_output"]
+ #[stable(feature = "fn_once_output", since = "1.12.0")]
+ type Output;
+
+ /// Performs the call operation.
+ #[unstable(feature = "fn_traits", issue = "29625")]
+ extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
+}
+```
+
+call_once 第一个参数是 self,它会转移 self 的所有权到 call_once 函数中
\ No newline at end of file
diff --git a/chapter11-closure/src/closure1-size.rs b/chapter11-closure/src/closure1-size.rs
new file mode 100644
index 0000000..b70f9d8
--- /dev/null
+++ b/chapter11-closure/src/closure1-size.rs
@@ -0,0 +1,35 @@
+mod r#closure2
+
+use std::{collections::HashMap, mem::size_of_val};
+fn main() {
+ // 长度为 0
+ let c1 = || println!("hello world!");
+ // 和参数无关,长度也为 0
+ let c2 = |i: i32| println!("hello: {}", i);
+ let name = String::from("Danny");
+ let name1 = name.clone();
+ let mut table = HashMap::new();
+ table.insert("hello", "world");
+ // 如果捕获一个对变量 name 的引用,这个引用是 &String,长度为 8
+ let c3 = || println!("hello: {}", name);
+ // 捕获移动的数据 name1(长度 24) + table(长度 48),closure 长度 72
+ let c4 = move || println!("hello: {}, {:?}", name1, table);
+ let name2 = name.clone();
+ // 和局部变量无关,捕获了一个 String name2,closure 长度 24
+ let c5 = move || {
+ let x = 1;
+ let name3 = String::from("lindsey");
+ println!("hello: {}, {:?}, {:?}", x, name2, name3);
+ };
+
+ // 不带 move 时,闭包捕获的是对应自由变量的引用;带 move 时,对应自由变量的所有权会被移动到闭包结构中
+ println!(
+ "c1: {}, c2: {}, c3: {}, c4: {}, c5: {}, main: {}",
+ size_of_val(&c1),
+ size_of_val(&c2),
+ size_of_val(&c3),
+ size_of_val(&c4),
+ size_of_val(&c5),
+ size_of_val(&main),
+ )
+}
diff --git a/chapter11-closure/src/closure2-fnonce.rs b/chapter11-closure/src/closure2-fnonce.rs
new file mode 100644
index 0000000..d15cd4a
--- /dev/null
+++ b/chapter11-closure/src/closure2-fnonce.rs
@@ -0,0 +1,28 @@
+fn main() {
+ let name = String::from("Danny");
+
+ // 这个闭包会 clone 内部的数据返回,所以它不是 FnOnce
+ let c = move |greeting: String| (greeting, name.clone());
+
+ // 所以 c1 可以被调用多次
+
+ println!("c1 call once: {:?}", c("您好".into()));
+ println!("c1 call twice: {:?}", c("bonjour".into()));
+
+ // 然而一旦它被当成 FnOnce 被调用,就无法被再次调用
+ println!("result: {:?}", call_once("hi".into(), c));
+
+ // 无法再次调用
+ // let result = c("hi".to_string());
+
+ // Fn 也可以被当成 FnOnce 调用,只要接口一致就可以
+ println!("result: {:?}", call_once("hola".into(), not_closure));
+}
+
+fn call_once(arg: String, c: impl FnOnce(String) -> (String, String)) -> (String, String) {
+ c(arg)
+}
+
+fn not_closure(arg: String) -> (String, String) {
+ (arg, "Rosie".into())
+}