Merge branch 'sunface:main' into main

pull/289/head
1132719438 3 years ago committed by GitHub
commit 3ecde633e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -8,7 +8,7 @@
- 知乎: [支持章节内目录跳转,很好用!](https://www.zhihu.com/column/c_1452781034895446017) - 知乎: [支持章节内目录跳转,很好用!](https://www.zhihu.com/column/c_1452781034895446017)
- 最近修订: 新增章节 [为async疑难杂症把把脉](https://zhuanlan.zhihu.com/p/460749920) - 最近修订: 新增章节 [Tokio使用指南 - 总览](https://zhuanlan.zhihu.com/p/460984955)
- Rust 版本: Rust edition 2021 - Rust 版本: Rust edition 2021
- QQ 交流群1009730433 - QQ 交流群1009730433

@ -2,6 +2,7 @@
[进入Rust编程世界](into-rust.md) [进入Rust编程世界](into-rust.md)
[关于本书](about-book.md) [关于本书](about-book.md)
[避免从入门到放弃](sth-you-should-not-do.md)
## Getting started ## Getting started
@ -92,17 +93,19 @@
- [高阶特征约束(HRTB) todo](advance/hrtb.md) - [高阶特征约束(HRTB) todo](advance/hrtb.md)
## 专题内容,每个专题都配套一个小型项目进行实践 ## 专题内容,每个专题都配套一个小型项目进行实践
- [async/await异步编程 doing](async/intro.md) - [async/await异步编程](async/intro.md)
- [async编程入门](async/getting-started.md) - [async编程入门](async/getting-started.md)
- [底层探秘: Future执行与任务调度](async/future-excuting.md) - [底层探秘: Future执行与任务调度](async/future-excuting.md)
- [定海神针Pin和Unpin](async/pin-unpin.md) - [定海神针Pin和Unpin](async/pin-unpin.md)
- [asyncawait和Stream流处理](async/async-await.md) - [async/await和Stream流处理](async/async-await.md)
- [同时运行多个Future](async/multi-futures-simultaneous.md) - [同时运行多个Future](async/multi-futures-simultaneous.md)
- [一些疑难问题的解决办法](async/pain-points-and-workarounds.md) - [一些疑难问题的解决办法](async/pain-points-and-workarounds.md)
- [实践应用Async Web服务器](async/web-server.md) - [实践应用Async Web服务器](async/web-server.md)
- [tokio todo](async/tokio/intro.md)
- [基本用法](async/tokio/basic.md) - [tokio使用指南 doing](tokio/intro.md)
- [异步消息流](async/tokio/stream.md)) - [tokio简介](tokio/overview.md)
<!-- - [基本用法](tokio/basic.md)
- [异步消息流](tokio/stream.md)) -->
- [Rust最佳实践 doing](practice/intro.md) - [Rust最佳实践 doing](practice/intro.md)
- [日常开发三方库精选](practice/third-party-libs.md) - [日常开发三方库精选](practice/third-party-libs.md)
@ -140,6 +143,8 @@
- [无处不在的迭代器](pitfalls/iterator-everywhere.md) - [无处不在的迭代器](pitfalls/iterator-everywhere.md)
- [线程间传递消息导致主线程无法结束](pitfalls/main-with-channel-blocked.md) - [线程间传递消息导致主线程无法结束](pitfalls/main-with-channel-blocked.md)
- [如何实现一个链表]()
- [进阶类型转换](converse/intro.md) - [进阶类型转换](converse/intro.md)
- [枚举和整数](converse/enum-int.md) - [枚举和整数](converse/enum-int.md)

@ -1 +0,0 @@
# 定海神针-tokio包

@ -34,9 +34,10 @@ struct User {
sign_in_count: 1, sign_in_count: 1,
}; };
``` ```
有几点值得注意: 有几点值得注意:
1. 初始化实例时,需要为**每个字段**都进行初始化 1. 初始化实例时,**每个字段**都需要进行初始化
2. 初始化时的字段顺序无需按照定义的顺序来 2. 初始化时的字段顺序不需要和结构体定义时的顺序一致
#### 访问结构体字段 #### 访问结构体字段
通过`.`操作符即可访问结构体实例内部的字段值,也可以修改它们: 通过`.`操作符即可访问结构体实例内部的字段值,也可以修改它们:
@ -50,7 +51,7 @@ struct User {
user1.email = String::from("anotheremail@example.com"); user1.email = String::from("anotheremail@example.com");
``` ```
需要注意的是,必须要将整个结构体都声明为可变的才能修改其中的字段Rust不允许单独将某个字段标记为可变: `let mut user1 = User {...}`. 需要注意的是,必须要将结构体实例声明为可变的才能修改其中的字段Rust不支持将某个结构体某个字段标记为可变.
#### 简化结构体创建 #### 简化结构体创建
下面的函数类似一个构建函数,返回了`User`结构体的实例: 下面的函数类似一个构建函数,返回了`User`结构体的实例:

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

@ -0,0 +1,61 @@
# 避免从入门到放弃
很多人都在学Rust ing也有很多人在放弃 ing。想要顺利学完 Rust大家需要谨记本文列出的内容否则这极有可能是又又又一次从入门到放弃之旅。
Rust 是一门全新的语言,它会带给你前所未有的体验,提升你的通用编程水平,甚至于赋予你全新的编程思想。在此时此刻,大家可能还半信半疑,但是当学完它再回头看时,可能你也会认同这些貌似浮夸的赞美。
## 避免试一试的心态
在学习Go、Python等编程语言时你可能会一边工作一边轻松愉快的学习它们但是Rust不行。原因如文章开头所说在学习Rust的同时你会收获很多语言之外的知识因此 Rust 在入门阶段比很多编程语言要更难,但是一旦入门,你将收获一个全新的自己,成为一个更加优秀的程序员。
在学习过程中一开始可能会轻松愉快但是在开始Rust核心概念时(所有权、借用、生命周期、智能指针等),难度可能会陡然提升,此时就需要认真对待起来,否则会为后面埋下很多难以填补的坑,结果最后你可能只有两个选择:重新学一遍 or 放弃。
因此,在学习过程中,给大家三点建议:
- 要提前做好会遇到困难的准备因为如上面所说学习Rust不仅仅是在学习一门编程语言
- 不要抱着试一试的心态去试一试否则是浪费时间和消耗学习的激情作为连续六年全世界最受喜欢的语言Rust不仅仅是值得试一试 :)
- 深入学习一本好书或教程
总之, Rust 入门难,但是在你一次次克服艰难险阻的同时,也一次次收获了与众不同的编程经验,最后历经九九八十一难,立地成大佬。 给自己一个机会,也给 Rust 一个机会
## 深入学习一本好书
Rust跟其它语言不一样你无法看了一遍语法然后就能上手写代码我说的就是对比 Go 语言,后者的简单易用是有目共睹的。
这些年,我遇到过太多在网上看了一遍菜鸟教程(或其它简易教程)就上手写demo甚至项目的同学无一例外都各种碰壁、趟坑最后要么放弃要么回炉重造之前的时间和精力基本等同浪费。
因此大家一定要舍得投入时间沉下心去读一本好书这本书会带你深入浅出地学习使用Rust所需的各种知识还会带你提前趟坑这些坑往往是需要大量的时间才能领悟的。
在以前我可能会推荐看官方那本书的英文原版 + async book + nomicon这几本书的组合但是现在有了一本更适合中国用户的书籍那就是 [<<Rust语言圣经>>](https://github.com/sunface/rust-course),内容好坏大家一读即知,光就文字而言,那绝对是行云流水般的阅读体验,可以极大提升你的学习效率,也不再因为反复读也读不懂一句话而烦闷不堪。
## 千万别从链表或图开始练手
CS课程中我们会学习大量的常用数据结构和算法因此大家都养成了一种好习惯学习一门新语言先用它写个链表或图试试。
我的天在Rust中千万别这么干你是在扼杀自己之前的努力因为不像其它语言链表在Rust中简直是地狱一般的难度我见过太多英雄好汉难过链表关最终黯然退幕。我不希望正在阅读此文的你也成为其中一个 :
这些自引用类型的数据结构(包含了字段,该字段又引用了自身)它们是恶魔它们不仅仅在蹂躏着新手还在折磨着老手有意思的是它们的难恰恰是Rust的优点导致的无gc也无手动内存管理内存安全。
这两点的实现并不是凭空产生的而是通过Rust一套非常强大、优美的机制提供了支持这些机制一旦你学到就会被它巧妙的构思和设计而征服进而被 Rust 深深吸引!但是一切选择都有利弊,这种机制的弊端就在于实现链表这种数据结构时,会变得非常非常复杂。
你需要糅合各种知识才能解决这个问题但是这显然不是一个新手应该独自去面对的。总之不会链表对于Rust的学习和写项目真的没有任何影响直接使用大神已经写好的数据结构包就可以。
如果想要练手,我们可以换个方向开始,当然如果你就是喜欢征服困难,那没问题,就从链表开始。但是无论选择哪个,之前提到的那本书都会给你莫大的帮助,包括如何实现一个链表!
## 仔细阅读编译错误
在一些编程语言中,你可能习惯了编译器给出的错误只要看前面(或后面)几行就行,大部分是不怎么用到的信息,总之编译器总感觉笨笨的。
但是 Rust 不是,它为我们提供了一个强大无比的编译器,而且会提示我们该如何修改代码以解决错误,简直就是一个优秀的老师!
因此在使用 Rust 过程中如果错误你不知该如何解决不妨仔细阅读下编译器或者IDE给出的错误提示绝大多数时候你都可以通过这些提示顺利的解决问题。
同时也不要忽略编译器给出的警告信息(warnings),因为里面包含了 `cargo clippy` 给出的 `lint` 提示,这些提示不仅仅包含代码风格,甚至包含了一些隐藏很深的错误!至于这些错误为何不是 `error` 形式出现,随着学习的深入,你将逐渐理解 Rust 的各种设计选择,包括这个问题。
## 不要强制自己使用其它编程语言的最佳实践来写Rust
大多数其它编程语言适用的最佳实践在Rust中也可以很好的使用但是 Rust 并不是一门专门的面向对象或者函数式语言,因此在使用自己喜欢的编程风格时,也要考虑遵循 Rust 应有的实践。
例如纯面向对象或纯函数式编程,在 Rust 中就并不是一个很好的选择。如果你有过Go语言的编程经验相信能更加理解我这里想表达的含义。
不过大家也不用担心,在书中我们以专题的形式专门讲解 Rust 的最佳实践,看完后自然就明白了。
## 总结
对于新手而言,最应该避免的就是从**链表开始练手**,最应该做的就是认真仔细地学习一本优秀的书。
总之,认真学 Rust既然选择了就相信自己你的前方会是星辰大海

@ -1 +1,3 @@
# 集成测试 # 集成测试
https://www.reddit.com/r/rust/comments/s9zs5c/how_do_you_debug_a_rust_library_after_compilation/

@ -0,0 +1,6 @@
# Tokio使用指南
在上一个章节中,我们提到了 Rust 异步编程的限制,其中之一就是你必须引入社区提供的异步运行时,其中最有名的就是 `tokio`
在本章中,我们一起来看看 `tokio` 到底有什么优势,以及该如何使用它。
> 本章在内容上大量借鉴和翻译了tokio官方文档[Tokio Tutorial](https://tokio.rs/tokio/tutorial), 但是重新组织了内容形式并融入了很多自己的见解和感悟,给大家提供更好的可读性和知识扩展性

@ -0,0 +1,75 @@
# tokio简介
对于 Async Rust最最重要的莫过于底层的异步运行时它提供了执行器、任务调度、异步API等核心服务。简单来说使用 Rust 提供的 `async/.await` 特性编写的异步代码要运行起来,就必须依赖于异步运行时,否则这些代码将毫无用处。
## 异步运行时
Rust 语言本身只提供了异步编程所需的基本特性,例如 `async/.await` 关键字,标准库中的 `Future` 特征,官方提供的 `futures` 实用库,这些特性单独使用没有任何用处,因此我们需要一个运行时来将这些特性实现的代码运行起来。
异步运行时是由 Rust 社区提供的,它们的核心是一个 `reactor` 和一个或多个 `executor`(执行器):
- `reactor` 用于提供外部事件的订阅机制,例如 `I/O` 、进程间通信、定时器等
- `executor` 在上一章我们有过深入介绍,它用于调度和执行相应的任务( `Future` )
目前最受欢迎的几个运行时有:
- [`tokio`](https://github.com/tokio-rs/tokio),目前最受欢迎的异步运行时,功能强大,还提供了异步所需的各种工具(例如 tracing )、网络协议框架(例如 HTTPgRPC )等等
- [`async-std`](https://github.com/async-rs/async-std),最大的优点就是跟标准库兼容性较强
- [`smol`](https://github.com/smol-rs/smol), 一个小巧的异步运行时
但是,大浪淘沙,留下的才是金子,随着时间的流逝,`tokio`越来越亮眼,无论是性能、功能还是社区、文档,它在各个方面都异常优秀,时至今日,可以说已成为事实上的标准。
#### 异步运行时的兼容性
为何选择异步运行时这么重要?不仅仅是它们在功能、性能上存在区别,更重要的是当你选择了一个,往往就无法切换到另外一个,除非异步代码很少。
使用异步运行时,往往伴随着对它相关的生态系统的深入使用,因此耦合性会越来越强,直至最后你很难切换到另一个运行时,例如 `tokio``async-std` ,就存在这种问题。
如果你实在有这种需求,可以考虑使用 [`async-compat`](https://github.com/smol-rs/async-compat),该包提供了一个中间层,用于兼容 `tokio` 和其它运行时。
#### 结论
相信大家看到现在,心中应该有一个结论了。首先,运行时之间的不兼容性,让我们必须提前选择一个运行时,并且在未来坚持用下去,那这个运行时就应该是最优秀、最成熟的那个,`tokio` 几乎成了不二选择,当然 `tokio` 也有自己的问题:更难上手和运行时之间的兼容性。
如果你只用 `tokio` 那兼容性自然不是问题至于难以上手Rust 这么难,我们都学到现在了,何况区区一个异步运行时,在本书的帮忙下,这些都不再是个问题:)
## tokio简介
Tokio是一个纸醉金迷之地只要有钱就可以为所欲为抱歉走错片场了。`Tokio` 是 Rust 最优秀的异步运行时框架,它提供了写异步网络服务所需的几乎所有功能,不仅仅适用于大型服务器,还适用于小型嵌入式设备,它主要由以下组件构成:
- 多线程版本的异步运行时,可以运行使用 `async/.await` 编写的代码
- 标准库中阻塞API的异步版本例如`thread::sleep`会阻塞当前线程,`tokio`中就提供了相应的异步实现版本
- 构建异步编程所需的生态,甚至还提供了 [`tracing`](https://github.com/tokio-rs/tracing) 用于日志和分布式追踪, 提供 [`console`](https://github.com/tokio-rs/console) 用于 Debug 异步编程
### 优势
下面一起来看看使用 `tokio` 能给你提供哪些优势。
**高性能**
因为快所以快,前者是 Rust 快,后者是 `tokio` 快。 `tokio` 在编写时充分利用了 Rust 提供的各种零抽象和高性能特性,而且贯彻了 Rust 的牛逼思想:如果你选择手写代码,那么最好的结果就是跟 `tokio` 一样快!
以下是一张官方提供的性能参考图,大致能体现出 `tokio` 的性能之恐怖:
<img alt="tokio performance" src="/img/tokio-01.png" class="center" />
**高可靠**
Rust 语言的安全可靠性顺理成章的影响了 `tokio` 的可靠性,曾经有一个调查给出了令人乍舌的[结论](https://www.zdnet.com/article/microsoft-70-percent-of-all-security-bugs-are-memory-safety-issues/)软件系统70%的高危漏洞都是由内存不安全性导致的。
在 Rust 提供的安全性之外,`tokio` 还致力于提供一致性的行为表现:无论你何时运行系统,它的预期表现和性能都是一致的,例如不会出现莫名其妙的请求延迟或响应时间大幅增加。
**简单易用**
通过 Rust 提供的 `async/await` 特性,编写异步程序的复杂性相比当初已经大幅降低,同时 `tokio` 还为我们提供了丰富的生态,进一步大幅降低了其复杂性。
同时 `tokio` 遵循了标准库的命名规则,让熟悉标准库的用户可以很快习惯于 `tokio` 的语法,再借助于 Rust 强大的类型系统,用户可以轻松地编写和交付正确的代码。
**使用灵活性**
`tokio` 支持你灵活的定制自己想要的运行时,例如你可以选择多线程 + 任务盗取模式的复杂运行时,也可以选择单线程的轻量级运行时。总之,几乎你的每一种需求在 `tokio` 中都能寻找到支持(画外音:强大的灵活性需要一定的复杂性来换取,并不是免费的午餐)。
### 劣势
虽然 `tokio` 对于大多数需要并发的项目都是非常适合的,但是确实有一些场景它并不适合使用:
- 并行运行CPU密集型的任务`tokio` 非常适合于IO密集型任务这些IO任务的绝大多数时间都用于阻塞等待IO的结果而不是刷刷刷的单烤CPU。如果你的应用是CPU密集型(例如并行计算),建议使用 [`rayon`](https://github.com/rayon-rs/rayon)当然对于其中的IO任务部分你依然可以混用 `tokio`
- 读取大量的文件, 读取文件的瓶颈主要在于操作系统因为OS没有提供异步文件读取接口大量的并发并不会提升文件读取的并行性能反而可能会造成不可忽视的性能损耗因此建议使用线程(或线程池)的方式
- 发送HTTP请求`tokio` 的优势是给予你并发处理大量任务的能力,对于这种轻量级 HTTP 请求场景,`tokio` 除了增加你的代码复杂性,并无法带来什么额外的优势。因此,对于这种场景,你可以使用 [`reqwest`](https://github.com/seanmonstar/reqwest) 库,它会更加简单易用。
## 总结
离开三方开源社区提供的异步运行时, `async/await` 什么都不是,甚至还不如一堆破铜烂铁,除非你选择根据自己的需求手撸一个。
`tokio` 就是那颗皇冠上的夜明珠,也是值得我们投入时间去深入学习的开源库,它的设计原理和代码实现都异常优秀,在之后的章节中,我们将对其进行深入学习和剖析,敬请期待。

@ -0,0 +1,3 @@
## 性能
https://www.reddit.com/r/rust/comments/lg0a7b/benchmarking_tokio_tasks_and_goroutines/

@ -0,0 +1 @@
https://www.reddit.com/r/rust/comments/s9yugv/compile_rust_to_wasm_and_import_as_typescript/
Loading…
Cancel
Save