diff --git a/src/async-rust/tokio/spawning.md b/src/async-rust/tokio/spawning.md index 9682319e..70efd828 100644 --- a/src/async-rust/tokio/spawning.md +++ b/src/async-rust/tokio/spawning.md @@ -2,14 +2,23 @@ 同志们,抓稳了,我们即将换挡提速,通向 `mini-redis` 服务端的高速之路已经开启。 -不过在开始之前,先来做点收尾工作:上一章节中,我们实现了一个简易的 `mini-redis` 客户端并支持了 `SET`/`GET` 操作, 现在将该[代码](https://course.rs/tokio/getting-startted.html#分析未到代码先行)移动到 `example` 文件夹下,因为我们这个章节要实现的是服务器,后面可以用之前客户端示例对我们的服务器端进行测试: +不过在开始之前,先来做点收尾工作:上一章节中,我们实现了一个简易的 `mini-redis` 客户端并支持了 `SET`/`GET` 操作, 现在将该[代码](https://course.rs/async-rust/tokio/getting-startted.html#分析未到代码先行)移动到 `examples` 文件夹下,因为我们这个章节要实现的是服务器,后面可以通过运行 `example` 的方式,用之前客户端示例对我们的服务器端进行测试: ```shell $ mkdir -p examples $ mv src/main.rs examples/hello-redis.rs ``` -然后再重新创建一个空的 `src/main.rs` 文件,至此换挡已经完成,提速正式开始。 +并在 `Cargo.toml` 里添加 `[[example]]` 说明。关于 `example` 的详细说明,可以在[Cargo使用指南](https://course.rs/cargo/reference/cargo-target.html#示例对象examples)里进一步了解。 + +```toml +[[example]] +name = "hello-redis" +path = "examples/hello-redis.rs" +``` + + +然后再重新创建一个空的 `src/main.rs` 文件,至此替换文档已经完成,提速正式开始。 ## 接收 sockets @@ -57,7 +66,7 @@ async fn process(socket: TcpStream) { cargo run ``` -此时服务器会处于循环等待以接收连接的状态,接下来在一个新的终端窗口中启动上一章节中的 `redis` 客户端,由于相关代码已经放入 `examples` 文件夹下,因此我们可以使用 `-- example` 来指定运行该客户端示例: +此时服务器会处于循环等待以接收连接的状态,接下来在一个新的终端窗口中启动上一章节中的 `redis` 客户端,由于相关代码已经放入 `examples` 文件夹下,因此我们可以使用 `--example` 来指定运行该客户端示例: ```shell $ cargo run --example hello-redis @@ -314,6 +323,6 @@ async fn process(socket: TcpStream) { 使用 `cargo run` 运行服务器,然后再打开另一个终端窗口,运行 `hello-redis` 客户端示例: `cargo run --example hello-redis`。 -Bingo,在看了这么多原理后,我们终于迈出了小小的第一步,并获取到了存在 `HashMap` 中的值: `got value from the server; result=Some(b"world")`。 +Bingo,在看了这么多原理后,我们终于迈出了小小的第一步,并获取到了存在 `HashMap` 中的值: `从服务器端获取到结果=Some(b"world")`。 但是问题又来了:这些值无法在 TCP 连接中共享,如果另外一个用户连接上来并试图同时获取 `hello` 这个 `key`,他将一无所获。 diff --git a/src/basic/collections/vector.md b/src/basic/collections/vector.md index beb2460e..e66c8744 100644 --- a/src/basic/collections/vector.md +++ b/src/basic/collections/vector.md @@ -104,7 +104,7 @@ let does_not_exist = v.get(100); ##### 同时借用多个数组元素 -既然涉及到借用数组元素,那么很可能会遇到同时借用多个数组元素的情况,还记得在[所有权和借用](https://course.rs/basic/ownership/borrowing.html#借用规则总结)章节咱们讲过的借用规则嘛?如果记得,就来看看下面的代码:) +既然涉及到借用数组元素,那么很可能会遇到同时借用多个数组元素的情况,还记得在[所有权和借用](https://course.rs/basic/ownership/borrowing.html#借用规则总结)章节咱们讲过的借用规则嘛?如果记得,就来看看下面的代码 :) ```rust let mut v = vec![1, 2, 3, 4, 5]; @@ -236,4 +236,4 @@ fn main() { ## 课后练习 -> [Rust By Practice](https://zh.practice.rs/collections/vector.html),支持代码在线编辑和运行,并提供详细的[习题解答](https://github.com/sunface/rust-by-practice)。 \ No newline at end of file +> [Rust By Practice](https://zh.practice.rs/collections/vector.html),支持代码在线编辑和运行,并提供详细的[习题解答](https://github.com/sunface/rust-by-practice)。 diff --git a/src/basic/ownership/ownership.md b/src/basic/ownership/ownership.md index f1d1939c..e520e58d 100644 --- a/src/basic/ownership/ownership.md +++ b/src/basic/ownership/ownership.md @@ -75,7 +75,7 @@ int* foo() { 理解了堆栈,接下来看一下*关于所有权的规则*,首先请谨记以下规则: -> 1. Rust 中每一个值都被一个变量所拥有,该变量被称之为值的所有者 +> 1. Rust 中每一个值都被一个变量所拥有,该变量被称为值的所有者 > 2. 一个值同时只能被一个变量所拥有,或者说一个值只能拥有一个所有者 > 3. 当所有者(变量)离开作用域范围时,这个值将被丢弃(drop) @@ -196,7 +196,7 @@ error[E0382]: use of moved value: `s1` 现在再回头看看之前的规则,相信大家已经有了更深刻的理解: -> 1. Rust 中每一个值都被一个变量所拥有,该变量被称之为值的所有者 +> 1. Rust 中每一个值都被一个变量所拥有,该变量被称为值的所有者 > 2. 一个值同时只能被一个变量所拥有,或者说一个值只能拥有一个所有者 > 3. 当所有者(变量)离开作用域范围时,这个值将被丢弃(drop) diff --git a/src/cargo/reference/build-script/intro.md b/src/cargo/reference/build-script/intro.md index f568868e..a25e60f1 100644 --- a/src/cargo/reference/build-script/intro.md +++ b/src/cargo/reference/build-script/intro.md @@ -24,7 +24,7 @@ fn main() { 关于构建脚本的一些使用场景如下: - 构建 C 依赖库 -- 在操作系统中寻找制定的 C 依赖库 +- 在操作系统中寻找指定的 C 依赖库 - 根据某个说明描述文件生成一个 Rust 模块 - 执行一些平台相关的配置 diff --git a/src/index-list.md b/src/index-list.md index 66045c00..84161115 100644 --- a/src/index-list.md +++ b/src/index-list.md @@ -165,9 +165,20 @@ ## H -| 名称 | 关键字 | 简介 | -| ---- | ------ | ---- | -| | KWH | | +| 名称 | 关键字 | 简介 | +| --------------------- | -------- | --------------------------------------------------------------------------------------------------- | +| [HashMap] | 哈希类型 | `HashMap`,存储的是一一映射的 `KV` 键值对,并提供了平均复杂度为 `O(1)` 的查询方法 | +| [HashMap::new()] | | 创建 HashMap,需要手动通过 `use std::collections::HashMap;` 引入到我们当前的作用域中来 | +| `hash.insert(K, V)` | | 插入键值对,必须声明为 `mut` | +| [元组创建 HashMap] | | 使用迭代器和 collect 方法创建
`let teams_map: HashMap<_, _> = teams_list.into_iter().collect();` | +| [查询 HashMap] | | 通过 `get` 方法可以获取元素,返回一个 `Option<&T>` 类型 | +| [更新 HashMap 中的值] | | | +| | KWH | | + +[hashmap::new()]: https://course.rs/basic/collections/hashmap.html#使用-new-方法创建 +[元组创建 hashmap]: https://course.rs/basic/collections/hashmap.html#使用迭代器和-collect-方法创建 +[查询 hashmap]: https://course.rs/basic/collections/hashmap.html#查询-hashmap +[更新 hashmap 中的值]: https://course.rs/basic/collections/hashmap.html#更新-hashmap-中的值 [back](#head) @@ -362,15 +373,25 @@ ## V -| 名称 | 关键字 | 简介 | -| ----------------- | -------- | ---------------------------------------------------------------------------------------------- | -| [Vector 动态数组] | 动态数组 | `Vec`,动态数组允许你存储多个值,这些值在内存中一个紧挨着另一个排列。只能存储相同类型的元素 | -| `Vec::new` | | 创建动态数组 | -| `vec![]` | | 创建动态数组,能在创建时给予初始化值 | -| `Vec::push` | | 向数组尾部添加元素,必须声明为 `mut` | -| | KWV | | +| 名称 | 关键字 | 简介 | +| -------------------------- | -------- | ---------------------------------------------------------------------------------------------- | +| [Vector 动态数组] | 动态数组 | `Vec`,动态数组允许你存储多个值,这些值在内存中一个紧挨着另一个排列。只能存储相同类型的元素 | +| [Vec::new] | | 创建动态数组 | +| [vec!\[\]] | | 创建动态数组,能在创建时给予初始化值 | +| [vec.push] | | 向数组尾部添加元素,必须声明为 `mut` | +| [vec.get] | | 从 Vector 中读取元素,返回 `Option<&T>` | +| `&vec[index]` | | 从 Vector 中读取元素,使用下标索引访问,从 0 开始 | +| [迭代遍历 Vector 中的元素] | | 如果想要依次访问数组中的元素,可以使用迭代的方式去遍历数组 | +| [存储不同类型的元素] | | 通过使用枚举类型和特征对象来实现不同类型元素的存储 | +| | KWV | | [vector 动态数组]: https://course.rs/basic/collections/vector.html +[vec::new]: https://course.rs/basic/collections/vector.html#vecnew +[vec!\[\]]: https://course.rs/basic/collections/vector.html#vec +[vec.push]: https://course.rs/basic/collections/vector.html#更新-vector +[vec.get]: https://course.rs/basic/collections/vector.html#从-vector-中读取元素 +[迭代遍历 vector 中的元素]: https://course.rs/basic/collections/vector.html#迭代遍历-vector-中的元素 +[存储不同类型的元素]: https://course.rs/basic/collections/vector.html#存储不同类型的元素 [back](#head) diff --git a/src/rust-weekly.md b/src/rust-weekly.md index 020f3f7f..4478de98 100644 --- a/src/rust-weekly.md +++ b/src/rust-weekly.md @@ -5,141 +5,83 @@ Rust语言周刊精选全世界过去一周的优秀文章、新闻、开源项 > RustCn:https://hirust.cn, 公众号: Rust语言中文网 -# 「Rust 语言周刊」 第 12 期 · 2022-05-16 -Rust语言周刊精选全世界过去一周的优秀文章、新闻、开源项目和语言动态。 - -本周刊由 RustCn 社区倾情打造,其中, `[Zh]` 标识的中文资料由 Rust 翻译计划提供,并且原始的 Markdown 文档已[全部开源](https://github.com/rustlang-cn/rustt),欢迎大家阅读和订阅。 - -> RustCn:https://hirust.cn, 公众号: Rust语言中文网 +# 「Rust 语言周刊」 第 13 期 · 2022-05-22 - -
题图: Kani - Rust 代码检查工具
+ +
题图: 将 Ruby 的编译器移植到 Rust 上
#### 官方新闻 -1、[cc 正式成为官方库: 将 C/C++ 代码编译成 Rust 代码](https://github.com/rust-lang/cc-rs) +1、[Rust 发布 1.61 版本](https://blog.rust-lang.org/2022/05/19/Rust-1.61.0.html) -Rust 的生态环境正在快速发展中,但是就目前而言,我们依然需要经常去调用 C/C++ 代码,此时 cc 就成了必不可少的构建工具( 配合 build.rs )。 +在历次版本更迭中,Rust 的 `fn main` 函数经历了从返回单元类型 `()`,到返回 `Result`,这两个实际上都是通过 `Termination` 特征来返回一个状态码(Exit Status)。 -在之前的某期中,我们有提到该库的作者已经[无力继续维护](https://github.com/rust-lang/cc-rs/issues/663),原因是各种平台兼容的 PR 搞得他焦头烂额,因此希望找一个接盘侠,如果是普通的库就罢了,偏偏是这么重要的库、维护复杂度这么高的库,因此这件事变得异常棘手。 +但之前该特征并没有稳定,我们无法自定义想要的返回状态码,而新版本中中,该特征最终稳定下来。 -好在,在官方的协调和支持下,cc 最终转入了官方组织,并且在多个月后,重写开始合并 PR ! +除此之外还有对常量函数 (const fn) 的进一步支持,意味着我们可以将更多的任务放在编译期去执行了。 #### 开源项目 -1、[Fornjot 发布 0.6 版本:一个使用 Rust 编写的 CAD 应用](https://www.fornjot.app/blog/fornjot-0.6/) - -这个世界需要一个新的 CAD,嗯,不是我说的,是作者说的,虽然我对此表示怀疑 :D - -总之,这个库是一个基于代码扩展的 CAD 应用,但是大家一定不要被 0.6 这个版本号所忽悠,它还很早期!例如它终于在新版本中支持 Z 轴非垂直方向的建模了: - - - -2、[Kani: 一个全新的 Rust 代码检查工具](https://github.com/model-checking/kani) - -Rust 的编译器这么强大,我们为什么还需要另一个非官方的编译检查工具?岂不是没事找事嘛?其实不然,毕竟,面对 unsafe 代码,传统的编译器依然有些有心无力,即使已经有了 mio。 - -而 kami 可以帮助我们检查代码的内存安全、用户定义的危险断言、panics、算术溢出等非预期行为,而且 kami 可以和我们的测试代码紧密结合,例如下面的一使用示例: - -```rust -use my_crate::{function_under_test, meets_specification, precondition}; - -#[kani::proof] -fn check_my_property() { - // 创建一个非确定性的输入 - let input = kani::any(); +1、[BlindAI: 一个基于 Rust 的开源 AI 项目](https://blog.mithrilsecurity.io/introducing-blindai/) - //根据函数的先决条件来限制它 - kani::assume(precondition(input)); +谁说 Rust 在机器学习/人工智能方面是纯纯的菜鸟?这不,我们拥有了一个纯 Rust 编写的、现代化的...AI 模型部署工具,它可以把我们创建的 AI 模型方便地部署在远程服务器上,同时对终端用户提供安全的访问接口。 - // 调用需要被验证的函数 - let output = function_under_test(input); + - // 检查它是否满足特性需求 - assert!(meets_specification(input, output)); -} -``` +2、[Fyrox 发布 0.25 版本](https://fyrox.rs/blog/post/feature-highlights-0-25/) -3、[RepliByte: 一个开源的数据库同步服务](https://github.com/Qovery/replibyte) +对于 Fyrox 大家可能不熟悉,但是提到 rg3d,很多人可能就恍然大悟了。是的, fyrox 它的新名字( 我觉得新名字比老的要好,未来更好推广,但是还是建议大家在项目初期想一个好的名字,不然后面改名的隐形成本实在太高了!) -作为开发者而言,在测试环境伪造数据用于测试是一件头疼的事,而且还不能反映出真实世界的复杂性,出于自身的这个痛点,作者创建了 RepliByte。 - -它支持 Pg、Mysql、MongoDB 的数据备份和还原,还支持敏感数据替换、数据子集、传输过程中的数据压缩和加密等,最重要的:它是 Rust 写的,每次想起公司那个占用大量资源、基于 Java 的数据库同步中间件,我就... - -```shell -### 列出所有备份 -$ replibyte -c conf.yaml dump list - -type name size when compressed encrypted -PostgreSQL dump-1647706359405 154MB Yesterday at 03:00 am true true -PostgreSQL dump-1647731334517 152MB 2 days ago at 03:00 am true true -PostgreSQL dump-1647734369306 149MB 3 days ago at 03:00 am true true -``` - -```shell -### 还原最新的备份到本地数据库中 -$ replibyte -c conf.yaml dump restore local -v latest -i postgres -p 5432 - -### 还原最新的备份到远程数据库中 -$ replibyte -c conf.yaml dump restore remote -v latest -``` - - -4、[Stretch: 一个高性能的基于 flexbox 的 UI 布局库](https://github.com/DioxusLabs/stretch) - -严格来说,该库是一个 fork 项目,之前的项目已经很久不再维护了,现在 [Dioxus]() 扛起了大旗,目标是为跨平台的布局提供一个坚定的基石,尤其是移动端,未来还会提供除了 `flexbox` 外更多的布局方式。 - -项目目前处于初期阶段,主要是 Dioxus 和 BevyEngine 在参与,如果大家感兴趣,可以看看里面的 `good first issue`。 - -> 感谢 [Asura](https://github.com/asur4s) 同学提供的[消息源](https://github.com/rustlang-cn/rust-weekly/issues/6) +在新版本中,主要是引入了静态插件和脚本的支持,其中一个用途就是用来扩展游戏引擎,例如现在的 fyrox 支持在游戏编辑器中运行游戏了,就像很多知名的游戏引擎一样! + #### 精选文章 -1、[Zh] [Rust 与 OpenCV](https://github.com/rustlang-cn/Rustt/blob/main/Articles/%5B2022-05-08%5D%20Rust%20与%20OpenCV.md) +1、[我们是怎么将 Ruby 编译器移植到 Rust 的](https://shopify.engineering/porting-yjit-ruby-compiler-to-rust) + +在 [4 月份时](https://baijiahao.baidu.com/s?id=1730684175033094920&wfr=spider&for=pc),一个爆炸性的新闻出现:Ruby 的 YJIT 编译器使用 Rust 完成了移植,并等待合并到上游代码仓库中,但当时并没有给出太多的细节。 -Rust非常优秀,但是与 C/C++ 等巨头相比,它在社区生态上还只是一头初生牛犊,因此我们经常需要去使用 C/C++ 编写的库。友情提示:在 Rust 中使用 OpenCV 是可行的,但是首先,你可能需要拥有足够的意志力来克服遇到的各种问题,毕竟,这是一条未知之路。 +一个月后的这篇文章中,开发者团队终于解开了神秘的面纱,深入谈了下在移植过程中遇到问题以及收获的经验,值得一读。 -2、[编程语言是平台,而不是产品](https://kerkour.com/programming-languages-are-platforms) +2、[Rust和供应链](https://blog.logrocket.com/comparing-rust-supply-chain-safety-tools/) -现代社会,每个大公司都想做一个大而全的产品,这样就可以行成更强的护城河,让竞争对手无路可走,然而这种产品会牺牲稳定性和可靠性。那么问题来了,对于编程语言而言,适合吗? +嗒哒,你可能被题目所欺骗了,以为本文是写 Rust 和物流供应链的,实际上,这是一篇关于 Rust 依赖链的文章,作者貌似对于外部的一切都充满了不信任感,因此希望能发现 Rust 项目中到底使用了哪些库,以及相关的详细信息。 -3、[Xilem: 一个 Rust UI 架构](https://raphlinus.github.io/rust/gui/2022/05/07/ui-architecture.html) +如果大家读了后,觉得意犹未尽,这里也有一篇[相似的文章](https://insanitybit.github.io/2022/05/10/supply-chain-thoughts)。 -对于用户界面领域来说,Rust 语言提供了足够的吸引力,原因很简单,它能够同时交付安全性和性能。但是,如何找到一个好的 UI 架构,成为了相当大的挑战。 +3、[语言特性真的越多越好吗?](https://www.thecodedmessage.com/posts/2022-05-11-programming-multiparadigm/) -在其它语言适用的架构设计未必能很好的应用在 Rust 上,毕竟 Rust 的一些语言特性,学过的人都懂,一个自引用结构体都能整的人快乐无边 :P 基于此原因,一些人并不看好 Rust 在用户界面的未来,但是作者依然坚定的认为 Rust 就是未来。 +C++ 程序员经常引以为傲的一点就是 C++ 什么都能做,而且还是多编程范式的。如果你对此套路不熟悉,很容易就被其吸引,然后走上一条不归路。 -他之前的尝试( 包括当前的 [Druid](https://github.com/linebender/druid) 架构 !) 都存在或多或少的缺陷,因此在这篇文章中,他提出了一个全新的架构,非常值得一读! +看了本文后,至少这些套路你将了若指掌,以后互喷起来,不要太愉快 :P -4、[使用 Rust 进行有限状态机的建模](https://www.ramnivas.com/blog/2022/05/09/fsm-model-rust) +4、[使用 Rust 扩展 SQLite](https://ricardoanderegg.com/posts/extending-sqlite-with-rust/) -在文章中,作者介绍了如何运用 Rust 的所有权机制和静态编译的特点实现一个有限状态机( FSM )。 +SQLite 支持多种扩展机制,本文将通过扩展的方式,为 SQLite 实现 zstd 压缩。 -5、[算法面试很卷?](https://ada-x64.github.io/over-engineering/) +5、[深入 Rust 异步编程](https://conradludgate.com/posts/async) -作者在找工作中,现在的大环境意味着算法成了绕不过去的坎。那到底是用更高级的代码卷起来,还是躺平使用简单的实现,文中给出了他的选择。 +友情提示,阅读本文,首先你需要坚实的基础?其实不是,你需要一杯咖啡、一个躺椅和一盘小点心,因为它真的挺长。。 -6、[挑战:在 wifi 路由器上运行 Rust](https://blog.dend.ro/building-rust-for-routers/) +6、[Default 和 From](https://elijahcaine.me/rust-default-from/) -作者有一个 OpenWrt wifi 路由器,某一天无聊至极,盯着这个老朋友发呆ing,结果突发奇想,嘿,也许我可以在这上面运行个 Rust 项目。`hello world` 显然跟路由器八字不合,那就写一个 DNS 客户端吧。 +从 Python 到 Rust,作者发现代码变得冗长了不少,他的手已然累了。因此,本文将从工程性的角度出发,谈谈 `Default` 和 `From` 在实战中能发挥出什么威力。 +7、[游戏开发中的音频为何这么难](https://tesselode.github.io/articles/audio-libraries-considered-challenging/) -7、[Rust 中的 O(1) 泛型](https://peterkos.me/rust-const-generics/) +游戏由于 fps 的限制,导致了音视频的开发难度相对其他领域来说会更高一些,作者在编写 [kira](https://github.com/tesselode/kira/) 的过程中,深刻领悟了这一点。 -众所周知,Rust 的泛型是零开销的,原因在于编译器会把泛型展开为一个个具体的类型,以二进制文件膨胀的代价来换取性能,这个做法没有任何问题。但是如果我们想要更加强大的泛型特性呢?文中针对这一点,给出了一些有价值的观点,值得一看! +8、[系列] [链接 Rust 依赖包 1](https://blog.pnkfx.org/blog/2022/05/12/linking-rust-crates/) -8、[创业公司是否应该使用 Rust ?](https://www.shuttle.rs/blog/2021/10/08/building-a-startup-with-rust) +说到依赖包,应该没人不知道 `bin` 和 `lib` 类型,前者可以编译成可运行的二进制文件,后者则是常用的依赖库。但是以下这些呢?`bin, lib, dylib, staticlib, cdylib, rlib, and proc-macro`,能够一一说出的同学,请举个爪 =,= -可以说,每一个优秀的工程师,内心都有这样的梦想:使用技术创业,然后改变世界。就目前来看,Rust 拥有足够的吸引力,那我们是否可以选择 Rust 来作为初创公司的主要语言呢? +而文章要做的就是通过系列文章的方式带大家通过示例的方式熟悉这些库类型,本文是第一篇。 -9、[使用 Rust 实现去中心化的集群管理](https://quickwit.io/blog/chitchat/) -大家可能在想,分布式集群?难道不是在前面做一层负载均衡( nginx ),后面的集群就随便搞嘛?其实不是,你说的是无状态的分布式集群,这种集群的状态都存储在 redis、mysql 等数据库中,因此分布式很简单。 -但是对于有状态的集群来说,分布式就变成了一个相当棘手且有挑战的问题,最基本的:如果让集群中的节点感知到其它节点,在本文中,你能获取到一些灵感和解决方案。 @@ -147,8 +89,9 @@ Rust非常优秀,但是与 C/C++ 等巨头相比,它在社区生态上还只 目前所有的周刊都按照 `年/月/日期` 的方式归纳在 [docs](./docs) 目录下,大家可以按需查看。 +- [第 12 期](./docs/2022/5月/16.md) - [第 11 期](./docs/2022/5月/07.md) -- [第 10 期](./docs/2022/5月/07.md) +- [第 10 期](./docs/2022/4月/29.md) - [第 9 期](./docs/2022/4月/24.md) - [第 8 期](./docs/2022/4月/15.md) - [第 7 期](./docs/2022/4月/08.md)