diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 79290554..4d925654 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -293,7 +293,7 @@ - [标准库解析 todo](std/intro.md) - - [标准库使用最佳时间 todo](std/search.md) + - [标准库使用最佳实践 todo](std/search.md) - [Vector 常用方法 todo](std/vector.md) - [HashMap todo](std/hashmap.md) - [Iterator 常用方法 todo](std/iterator.md) diff --git a/src/advance/concurrency-with-threads/concurrency-parallelism.md b/src/advance/concurrency-with-threads/concurrency-parallelism.md index 8339c5d9..0afd54f9 100644 --- a/src/advance/concurrency-with-threads/concurrency-parallelism.md +++ b/src/advance/concurrency-with-threads/concurrency-parallelism.md @@ -41,7 +41,7 @@ #### 多核心并发 -当核心增多到 `N` 时,操作系统同时在进行的任务肯定远不止 `N` 个,这些任务将被放入 `M` 个线程队列中,接着交给 `N` 个 CPU 核心去执行,最后实现了 `M:N` 的处理模型,在这种情况下,**并发跟并行时同时在发生的,所有用户任务从表面来看都在并发的运行,其实实际上,同一时刻只有 `N` 个任务能被同时并行的处理**。 +当核心增多到 `N` 时,操作系统同时在进行的任务肯定远不止 `N` 个,这些任务将被放入 `M` 个线程队列中,接着交给 `N` 个 CPU 核心去执行,最后实现了 `M:N` 的处理模型,在这种情况下,**并发与并行是同时在发生的,所有用户任务从表面来看都在并发的运行,但实际上,同一时刻只有 `N` 个任务能被同时并行的处理**。 看到这里,相信大家已经明白两者的区别,那么我们下面给出一个正式的定义(该定义摘选自<<并发的艺术>>)。 diff --git a/src/async-rust/async/getting-started.md b/src/async-rust/async/getting-started.md index 68397f40..29c648c7 100644 --- a/src/async-rust/async/getting-started.md +++ b/src/async-rust/async/getting-started.md @@ -35,7 +35,7 @@ - **Future 在 Rust 中是惰性的**,只有在被轮询(`poll`)时才会运行, 因此丢弃一个 `future` 会阻止它未来再被运行, 你可以将`Future`理解为一个在未来某个时间点被调度执行的任务。 - **Async 在 Rust 中使用开销是零**, 意味着只有你能看到的代码(自己的代码)才有性能损耗,你看不到的(`async` 内部实现)都没有性能损耗,例如,你可以无需分配任何堆内存、也无需任何动态分发来使用 `async` ,这对于热点路径的性能有非常大的好处,正是得益于此,Rust 的异步编程性能才会这么高。 -- **Rust 没有内置异步调用所必须的运行时**,但是无需担心,Rust 社区生态中已经提供了非常优异的运行时实现,例如大明星 [`tokio`](https://tokio.rs) +- **Rust 没有内置异步调用所必需的运行时**,但是无需担心,Rust 社区生态中已经提供了非常优异的运行时实现,例如大明星 [`tokio`](https://tokio.rs) - **运行时同时支持单线程和多线程**,这两者拥有各自的优缺点,稍后会讲 #### Rust: async vs 多线程 @@ -56,7 +56,7 @@ > 若大家使用 tokio,那 CPU 密集的任务尤其需要用线程的方式去处理,例如使用 `spawn_blocking` 创建一个阻塞的线程去完成相应 CPU 密集任务。 > -> 至于具体的原因,不仅是上文说到的那些,还有一个是:tokio 是协作式地调度器,如果某个 CPU 密集的异步任务是通过 tokio 创建的,那理论上来说,该异步任务需要跟其它的异步任务交错执行,最终大家都得到了执行,皆大欢喜。但实际情况是,CPU 密集的任务很可能会一直霸着着 CPU,此时 tokio 的调度方式决定了该任务会一直被执行,这意味着,其它的异步任务无法得到执行的机会,最终这些任务都会因为得不到资源而饿死。 +> 至于具体的原因,不仅是上文说到的那些,还有一个是:tokio 是协作式地调度器,如果某个 CPU 密集的异步任务是通过 tokio 创建的,那理论上来说,该异步任务需要跟其它的异步任务交错执行,最终大家都得到了执行,皆大欢喜。但实际情况是,CPU 密集的任务很可能会一直霸占着 CPU,此时 tokio 的调度方式决定了该任务会一直被执行,这意味着,其它的异步任务无法得到执行的机会,最终这些任务都会因为得不到资源而饿死。 > > 而使用 `spawn_blocking` 后,会创建一个单独的 OS 线程,该线程并不会被 tokio 所调度( 被 OS 所调度 ),因此它所执行的 CPU 密集任务也不会导致 tokio 调度的那些异步任务被饿死 diff --git a/src/async-rust/tokio/stream.md b/src/async-rust/tokio/stream.md index 58b1773a..c7b07753 100644 --- a/src/async-rust/tokio/stream.md +++ b/src/async-rust/tokio/stream.md @@ -184,7 +184,7 @@ got = b"6" let messages = subscriber .into_stream() .filter_map(|msg| match msg { - Ok(msg) if msg.content.len() == 1 => msg.unwrap().content, + Ok(msg) if msg.content.len() == 1 => Some(msg.content), _ => None, }) .take(3); diff --git a/src/basic/match-pattern/all-patterns.md b/src/basic/match-pattern/all-patterns.md index 78776cf7..7c428825 100644 --- a/src/basic/match-pattern/all-patterns.md +++ b/src/basic/match-pattern/all-patterns.md @@ -97,7 +97,7 @@ Rust 知道 `'c'` 位于第一个模式的序列内,所以会打印出 `early ### 解构并分解值 -也可以使用模式来解构结构体、枚举、元组和引用。 +也可以使用模式来解构结构体、枚举、元组、数组和引用。 #### 解构结构体 @@ -274,6 +274,37 @@ let ((feet, inches), Point {x, y}) = ((3, 10), Point { x: 3, y: -10 }); 这种将复杂类型分解匹配的方式,可以让我们单独得到感兴趣的某个值。 +#### 解构数组 + +对于数组,我们可以用类似元组的方式解构,分为两种情况: + +定长数组 +```rust +let arr: [u16; 2] = [114, 514]; +let [x, y] = arr; + +assert_eq!(x, 114); +assert_eq!(y, 514); +``` + +不定长数组 +```rust +let arr: &[u16] = &[114, 514]; + +if let [x, ..] = arr { + assert_eq!(x, &114); +} + +if let &[.., y] = arr { + assert_eq!(y, 514); +} + +let arr: &[u16] = &[]; + +assert!(matches!(arr, [..])); +assert!(!matches!(arr, [x, ..])); +``` + ### 忽略模式中的值 有时忽略模式中的一些值是很有用的,比如在 `match` 中的最后一个分支使用 `_` 模式匹配所有剩余的值。 你也可以在另一个模式中使用 `_` 模式,使用一个以下划线开始的名称,或者使用 `..` 忽略所剩部分的值。 diff --git a/src/basic/match-pattern/pattern-match.md b/src/basic/match-pattern/pattern-match.md index 5b8837e5..3e823445 100644 --- a/src/basic/match-pattern/pattern-match.md +++ b/src/basic/match-pattern/pattern-match.md @@ -145,7 +145,7 @@ fn main() { `&(3, 5)` 会匹配模式 `&(x, y)`,因此 `x` 得到了 `3`,`y` 得到了 `5`。 -#### if 和 if let +#### let 和 if let 对于以下代码,编译器会报错: diff --git a/src/basic/result-error/panic.md b/src/basic/result-error/panic.md index 67a77416..c9904844 100644 --- a/src/basic/result-error/panic.md +++ b/src/basic/result-error/panic.md @@ -182,4 +182,4 @@ let home: IpAddr = "127.0.0.1".parse().unwrap(); ## 课后练习 -> [Rust By Practice](https://zh.practice.rs/result-panic/panic.html),支持代码在线编辑和运行,并提供详细的[习题解答](https://github.com/sunface/rust-by-practice)。 +> [Rust By Practice](https://zh.practice.rs/result-panic/panic.html),支持代码在线编辑和运行,并提供详细的[习题解答](https://github.com/sunface/rust-by-practice/blob/master/solutions/result-panic/panic.md)。 diff --git a/src/basic/result-error/result.md b/src/basic/result-error/result.md index 5d93daba..f607620b 100644 --- a/src/basic/result-error/result.md +++ b/src/basic/result-error/result.md @@ -13,7 +13,7 @@ enum Result { } ``` -泛型参数 `T` 代表成功时存入的正确值的类型,存放方式是 `Ok(T)`,`E` 代表错误是存入的错误值,存放方式是 `Err(E)`,枯燥的讲解永远不及代码生动准确,因此先来看下打开文件的例子: +泛型参数 `T` 代表成功时存入的正确值的类型,存放方式是 `Ok(T)`,`E` 代表错误时存入的错误值,存放方式是 `Err(E)`,枯燥的讲解永远不及代码生动准确,因此先来看下打开文件的例子: ```rust use std::fs::File; @@ -388,4 +388,4 @@ let x = try!(function_with_error()); ## 课后练习 -> [Rust By Practice](https://zh.practice.rs/result-panic/result.html),支持代码在线编辑和运行,并提供详细的[习题解答](https://github.com/sunface/rust-by-practice)。 +> [Rust By Practice](https://zh.practice.rs/result-panic/result.html),支持代码在线编辑和运行,并提供详细的[习题解答](https://github.com/sunface/rust-by-practice/blob/master/solutions/result-panic/result.md)。 diff --git a/src/basic/trait/advance-trait.md b/src/basic/trait/advance-trait.md index b469a6d9..7d8a4cd1 100644 --- a/src/basic/trait/advance-trait.md +++ b/src/basic/trait/advance-trait.md @@ -260,7 +260,7 @@ fn main() { } ``` -就像人类妈妈会给自己的宝宝起爱称一样,狗妈妈也会。狗妈妈称呼自己的宝宝为**Spot**,其它动物称呼狗宝宝为**puppy**,这个时候假如有动物不知道该称如何呼狗宝宝,它需要查询一下。 +就像人类妈妈会给自己的宝宝起爱称一样,狗妈妈也会。狗妈妈称呼自己的宝宝为**Spot**,其它动物称呼狗宝宝为**puppy**,这个时候假如有动物不知道该如何称呼狗宝宝,它需要查询一下。 `Dog::baby_name()` 的调用方式显然不行,因为这只是狗妈妈对宝宝的爱称,可能你会想到通过下面的方式查询其他动物对狗狗的称呼: diff --git a/src/basic/trait/trait.md b/src/basic/trait/trait.md index 8681e55f..0f1e89ef 100644 --- a/src/basic/trait/trait.md +++ b/src/basic/trait/trait.md @@ -1,8 +1,8 @@ # 特征 Trait -如果我们想定义一个文件系统,那么把该系统跟底层存储解耦是很重要的。文件操作主要包含三个:`open` 、`write`、`read`,这些操作可以发生在硬盘,可以发生在内存,还可以发生在网络IO甚至(...我实在编不下去了,大家来帮帮我)。总之如果你要为每一种情况都单独实现一套代码,那这种实现将过于繁杂,而且也没那个必要。 +如果我们想定义一个文件系统,那么把该系统跟底层存储解耦是很重要的。文件操作主要包含四个:`open` 、`write`、`read`、`close`,这些操作可以发生在硬盘,可以发生在内存,还可以发生在网络IO甚至(...我实在编不下去了,大家来帮帮我)。总之如果你要为每一种情况都单独实现一套代码,那这种实现将过于繁杂,而且也没那个必要。 -要解决上述问题,需要把这些行为抽象出来,就要使用 Rust 中的特征 `trait` 概念。可能你是第一次听说这个名词,但是不要怕,如果学过其他语言,那么大概率你听说过接口,没错,特征很类似接口。 +要解决上述问题,需要把这些行为抽象出来,就要使用 Rust 中的特征 `trait` 概念。可能你是第一次听说这个名词,但是不要怕,如果学过其他语言,那么大概率你听说过接口,没错,特征跟接口很类似。 在之前的代码中,我们也多次见过特征的使用,例如 `#[derive(Debug)]`,它在我们定义的类型(`struct`)上自动派生 `Debug` 特征,接着可以使用 `println!("{:?}", x)` 打印这个类型;再例如: @@ -95,7 +95,7 @@ sunface发表了微博好像微博没Tweet好用 上面我们将 `Summary` 定义成了 `pub` 公开的。这样,如果他人想要使用我们的 `Summary` 特征,则可以引入到他们的包中,然后再进行实现。 -关于特征实现与定义的位置,有一条非常重要的原则:**如果你想要为类型 `A` 实现特征 `T`,那么 `A` 或者 `T` 至少有一个是在当前作用域中定义的!**例如我们可以为上面的 `Post` 类型实现标准库中的 `Display` 特征,这是因为 `Post` 类型定义在当前的作用域中。同时,我们也可以在当前包中为 `String` 类型实现 `Summary` 特征,因为 `Summary` 定义在当前作用域中。 +关于特征实现与定义的位置,有一条非常重要的原则:**如果你想要为类型** `A` **实现特征** `T`**,那么** `A` **或者** `T` **至少有一个是在当前作用域中定义的!** 例如我们可以为上面的 `Post` 类型实现标准库中的 `Display` 特征,这是因为 `Post` 类型定义在当前的作用域中。同时,我们也可以在当前包中为 `String` 类型实现 `Summary` 特征,因为 `Summary` 定义在当前作用域中。 但是你无法在当前作用域中,为 `String` 类型实现 `Display` 特征,因为它们俩都定义在标准库中,其定义所在的位置都不在当前作用域,跟你半毛钱关系都没有,看看就行了。 diff --git a/src/compiler/pitfalls/lazy-iterators.md b/src/compiler/pitfalls/lazy-iterators.md index 5d51d2e0..5f0f4a3c 100644 --- a/src/compiler/pitfalls/lazy-iterators.md +++ b/src/compiler/pitfalls/lazy-iterators.md @@ -113,7 +113,7 @@ let resolvers: HashMap<_, _> = accounts ```rust let resolvers = account.into_iter().fold(HashMap::new(), |mut resolvers, a|{ - resolvers.entry(a.id).or_insert(Vec::new).push(a); + resolvers.entry(a.id).or_insert(Vec::new()).push(a); resolvers }); ``` diff --git a/src/test/benchmark.md b/src/test/benchmark.md index cda30735..13612526 100644 --- a/src/test/benchmark.md +++ b/src/test/benchmark.md @@ -100,7 +100,7 @@ test result: ok. 0 passed; 0 failed; 1 ignored; 1 measured; 0 filtered out; fini - 最好让 `iter` 之外的代码也具有幂等性,因为它也可能被 `benchmark` 运行多次 - 循环内的代码应该尽量的短小快速,因为这样循环才能被尽可能多的执行,结果也会更加准确 -#### 迷一般的性能结果 +#### 谜一般的性能结果 在写 `benchmark` 时,你可能会遇到一些很纳闷的棘手问题,例如以下代码: