Merge branch 'sunface:main' into main

pull/1126/head
Rustln 2 years ago committed by GitHub
commit 1ab32f5f10
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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)

@ -41,7 +41,7 @@
#### 多核心并发
当核心增多到 `N` 时,操作系统同时在进行的任务肯定远不止 `N` 个,这些任务将被放入 `M` 个线程队列中,接着交给 `N` 个 CPU 核心去执行,最后实现了 `M:N` 的处理模型,在这种情况下,**并发跟并行时同时在发生的,所有用户任务从表面来看都在并发的运行,其实实际上,同一时刻只有 `N` 个任务能被同时并行的处理**。
当核心增多到 `N` 时,操作系统同时在进行的任务肯定远不止 `N` 个,这些任务将被放入 `M` 个线程队列中,接着交给 `N` 个 CPU 核心去执行,最后实现了 `M:N` 的处理模型,在这种情况下,**并发与并行是同时在发生的,所有用户任务从表面来看都在并发的运行,但实际上,同一时刻只有 `N` 个任务能被同时并行的处理**。
看到这里,相信大家已经明白两者的区别,那么我们下面给出一个正式的定义(该定义摘选自<<并发的艺术>>)。

@ -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 调度的那些异步任务被饿死

@ -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);

@ -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` 中的最后一个分支使用 `_` 模式匹配所有剩余的值。 你也可以在另一个模式中使用 `_` 模式,使用一个以下划线开始的名称,或者使用 `..` 忽略所剩部分的值。

@ -145,7 +145,7 @@ fn main() {
`&(3, 5)` 会匹配模式 `&(x, y)`,因此 `x` 得到了 `3``y` 得到了 `5`
#### if 和 if let
#### let 和 if let
对于以下代码,编译器会报错:

@ -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)。

@ -13,7 +13,7 @@ enum Result<T, E> {
}
```
泛型参数 `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)。

@ -260,7 +260,7 @@ fn main() {
}
```
就像人类妈妈会给自己的宝宝起爱称一样,狗妈妈也会。狗妈妈称呼自己的宝宝为**Spot**,其它动物称呼狗宝宝为**puppy**,这个时候假如有动物不知道该如何呼狗宝宝,它需要查询一下。
就像人类妈妈会给自己的宝宝起爱称一样,狗妈妈也会。狗妈妈称呼自己的宝宝为**Spot**,其它动物称呼狗宝宝为**puppy**,这个时候假如有动物不知道该如何呼狗宝宝,它需要查询一下。
`Dog::baby_name()` 的调用方式显然不行,因为这只是狗妈妈对宝宝的爱称,可能你会想到通过下面的方式查询其他动物对狗狗的称呼:

@ -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` 特征,因为它们俩都定义在标准库中,其定义所在的位置都不在当前作用域,跟你半毛钱关系都没有,看看就行了。

@ -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
});
```

@ -100,7 +100,7 @@ test result: ok. 0 passed; 0 failed; 1 ignored; 1 measured; 0 filtered out; fini
- 最好让 `iter` 之外的代码也具有幂等性,因为它也可能被 `benchmark` 运行多次
- 循环内的代码应该尽量的短小快速,因为这样循环才能被尽可能多的执行,结果也会更加准确
#### 一般的性能结果
#### 一般的性能结果
在写 `benchmark` 时,你可能会遇到一些很纳闷的棘手问题,例如以下代码:

Loading…
Cancel
Save