Merge pull request #1 from sunface/main

sync
pull/733/head
Rustln 3 years ago committed by GitHub
commit 7895cf092b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -116,13 +116,17 @@
## 创作感悟 ## 创作感悟
截至目前Rust语言圣经已写了 170 余章110 余万字,历经 800 多个小时,其中每一个章节都是手动写就,没有任何机翻和质量上的妥协( 相信深入阅读过的读者都能体会到这一点 ),经过不懈地付出和努力,我们收获了 0 元钱 截至目前Rust语言圣经已写了 170 余章110 余万字,历经 800 多个小时,每一个章节都是手动写就,没有任何机翻和质量上的妥协( 相信深入阅读过的读者都能体会到这一点 )。
是的这本书没有带来一分钱的收入在用爱发电。事实上已经有不少出版社联系我出版了但我希望能保持自己的初心。Rust 要在国内真正发展起来,必须得有一群追逐梦想的人在做着不计付出的事情,而我希望自己能贡献一份微薄之力 曾经有读者问过 "这么好的书为何要开源,而不是出版?",原因很简单:**只有完全开源才能完美地呈现出我想要的教学效果**
但是要说完全无欲无求,那也是不可能的,谁看到读者的认可不欢喜?谁听到他人的称赞不雀跃?而我,最想获取的就是你们用指尖绘制的星空,这里繁星点点,每一颗都在鼓励着怀揣着开源梦想的程序员披荆斩棘、不断前行,不夸张的说,没有你们,开源世界就没有星光,自然也就不会有今天的开源盛世 总之Rust 要在国内真正发展起来,必须得有一些追逐梦想的人在做着不计付出的事情,而我希望自己能贡献一份微薄之力
因此,我恳请大家,如果觉得书还可以,就在你的指尖星空绘制一颗新的 🌟,指引我们继续砥砺前行。这个人世间,因善意而美好。 但是要说完全无欲无求,那也是不可能的,看到项目多了一颗 🌟,那感觉...棒极了,因为它代表了读者的认可和称赞。
你们用指尖绘制的星空,那里繁星点点,每一颗都在鼓励着怀揣着开源梦想的程序员披荆斩棘、不断前行,不夸张的说,没有你们,开源世界就没有星光,自然也就不会有今天的开源盛世。
因此,**我恳请大家,如果觉得书还可以,就在你的指尖星空绘制一颗新的 🌟,指引我们继续砥砺前行**。这个人世间,因善意而美好。
最后,能通过开源在茫茫人海中与大家相识,这感觉真好 :D 最后,能通过开源在茫茫人海中与大家相识,这感觉真好 :D

@ -7,7 +7,7 @@ src = "src"
[output.html] [output.html]
additional-css = ["theme/style2.css"] additional-css = ["theme/style2.css"]
additional-js = ["assets/custom.js", "assets/bigPicture.js"] additional-js = ["assets/custom.js", "assets/bigPicture.js"]
git-repository-url = "https://github.com/sunface/rust-course#创作感悟" git-repository-url = "https://github.com/sunface/rust-course"
edit-url-template = "https://github.com/sunface/rust-course/edit/main/{path}" edit-url-template = "https://github.com/sunface/rust-course/edit/main/{path}"
[output.html.playground] [output.html.playground]
@ -17,7 +17,7 @@ line-numbers = true
[output.html.fold] [output.html.fold]
enable = true enable = true
level = 1 level = 2
[rust] [rust]
edition = "2021" #在线运行用2021版本的 edition = "2021" #在线运行用2021版本的

@ -134,7 +134,7 @@
- [编写测试及控制执行](test/write-tests.md) - [编写测试及控制执行](test/write-tests.md)
- [单元测试和集成测试](test/unit-integration-test.md) - [单元测试和集成测试](test/unit-integration-test.md)
- [断言 assertion](test/assertion.md) - [断言 assertion](test/assertion.md)
- [用 Github Actions 进行持续集成](test/ci.md) - [用 GitHub Actions 进行持续集成](test/ci.md)
- [基准测试 benchmark](test/benchmark.md) - [基准测试 benchmark](test/benchmark.md)
- [Cargo 使用指南](cargo/intro.md) - [Cargo 使用指南](cargo/intro.md)
@ -263,28 +263,35 @@
- [HashMap todo](std/hashmap.md) - [HashMap todo](std/hashmap.md)
- [Iterator 常用方法 todo](std/iterator.md) - [Iterator 常用方法 todo](std/iterator.md)
- [CookBook todo](cases/intro.md) - [CookBook doing](cookbook/intro.md)
- [命令行解析 todo](cases/cmd.md) - [实用算法](cookbook/algos/intro.md)
- [配置文件解析 todo](cases/config.md) - [生成随机值](cookbook/algos/randomness.md)
- [编解码 todo](cases/encoding/intro.md) - [Vec 排序](cookbook/algos/sorting.md)
- [JSON](cases/encoding/json.md) - [命令行]()
- [CSV](cases/encoding/csv.md) - [参数解析](cookbook/cmd/parsing.md)
- [protobuf](cases/encoding/protobuf.md) - [终端输出格式化](cookbook/cmd/ansi.md)
- [文件系统 todo](cases/file/intro.md) - [压缩](cookbook/compression/intro.md)
- [文件读写](cases/file/file.md) - [使用.tar包](cookbook/compression/tar.md)
- [目录操作](cases/file/dir.md) - [配置文件解析 todo](cookbook/config.md)
- [网络通信 todo](cases/protocol/intro.md) - [编解码 todo](cookbook/encoding/intro.md)
- [HTTP](cases/protocol/http.md) - [JSON](cookbook/encoding/json.md)
- [TCP](cases/protocol/tcp.md) - [CSV](cookbook/encoding/csv.md)
- [UDP](cases/protocol/udp.md) - [protobuf](cookbook/encoding/protobuf.md)
- [gRPC](cases/protocol/grpc.md) - [文件系统 todo](cookbook/file/intro.md)
- [数据库访问 todo](cases/database.md) - [文件读写](cookbook/file/file.md)
- [正则表达式 todo](cases/regexp.md) - [目录操作](cookbook/file/dir.md)
- [加密解密 todo](cases/crypto.md) - [网络通信 todo](cookbook/protocol/intro.md)
- [时间日期](cases/date.md) - [HTTP](cookbook/protocol/http.md)
- [开发调试 todo](cases/dev/intro.md) - [TCP](cookbook/protocol/tcp.md)
- [日志](cases/dev/logs.md) - [UDP](cookbook/protocol/udp.md)
- [性能分析](cases/dev/profile.md) - [gRPC](cookbook/protocol/grpc.md)
- [数据库访问 todo](cookbook/database.md)
- [正则表达式 todo](cookbook/regexp.md)
- [加密解密 todo](cookbook/crypto.md)
- [时间日期](cookbook/date.md)
- [开发调试 todo](cookbook/dev/intro.md)
- [日志](cookbook/dev/logs.md)
- [性能分析](cookbook/dev/profile.md)
<!-- <!--
- [Rust区块链入门]() - [Rust区块链入门]()

@ -8,10 +8,10 @@ Rust语言真的好连续六年成为全世界最受欢迎的语言、没有G
至于 Rust 难学,那正是本书要解决的问题,如果看完后,你觉得没有学会 Rust可以找我们退款哦抱歉这是开源书那就退 🌟 吧:) 至于 Rust 难学,那正是本书要解决的问题,如果看完后,你觉得没有学会 Rust可以找我们退款哦抱歉这是开源书那就退 🌟 吧:)
如果看到这里,大家觉得这本书的介绍并没有吸引到你,不要立即放弃,强烈建议读一下[进入 Rust 编程世界](ttps://course.rs/into-rust.html),那里会有不一样的精彩。 如果看到这里,大家觉得这本书的介绍并没有吸引到你,不要立即放弃,强烈建议读一下[进入 Rust 编程世界](https://course.rs/into-rust.html),那里会有不一样的精彩。
> 本书完全开源,所有的文档内容都在 `Github` 上,至于里面还藏有什么秘密,大家点击右上角自行发现吧 > 本书完全开源,所有的文档内容都在 `GitHub` 上,至于里面还藏有什么秘密,大家点击右上角自行发现吧
> >
> 小秘密一: 你们可能会好奇,这本书到底与其它 Rust 书籍有[哪些不同](https://github.com/sunface/rust-course#教程简介) > 小秘密一: 你们可能会好奇,这本书到底与其它 Rust 书籍有[哪些不同](https://github.com/sunface/rust-course#教程简介)
@ -26,12 +26,29 @@ Rust语言真的好连续六年成为全世界最受欢迎的语言、没有G
同时社区还提供了一个优质的公众号: `studyrust`,里面的文章是由 [Rustt](https://rustt.org) 翻译组提供,搬运自国外优秀的 Rust 技术文章、学习资料、新闻资讯等。 同时社区还提供了一个优质的公众号: `studyrust`,里面的文章是由 [Rustt](https://rustt.org) 翻译组提供,搬运自国外优秀的 Rust 技术文章、学习资料、新闻资讯等。
- 社区官网:[https://studyrust.org](https://studyrust.org),正在建设中,暂时跳转到 Github 组织首页 - 社区官网:[https://studyrust.org](https://studyrust.org),正在建设中,暂时跳转到 GitHub 组织首页
- QQ交流群: 1009730433 - QQ交流群: 1009730433
- 微信公众号:`studyrust` - 微信公众号:`studyrust`
<img src="https://github.com/sunface/rust-course/blob/main/assets/studyrust公众号.png?raw=true" /> <img src="https://github.com/sunface/rust-course/blob/main/assets/studyrust公众号.png?raw=true" />
## 创作感悟
截至目前Rust语言圣经已写了 170 余章110 余万字,历经 800 多个小时,每一个章节都是手动写就,没有任何机翻和质量上的妥协( 相信深入阅读过的读者都能体会到这一点 )。
曾经有读者问过 "这么好的书为何要开源,而不是出版?",原因很简单:**只有完全开源才能完美地呈现出我想要的教学效果**。
总之Rust 要在国内真正发展起来,必须得有一些追逐梦想的人在做着不计付出的事情,而我希望自己能贡献一份微薄之力。
但是要说完全无欲无求,那也是不可能的,看到项目多了一颗 🌟,那感觉...棒极了,因为它代表了读者的认可和称赞。
你们用指尖绘制的星空,那里繁星点点,每一颗都在鼓励着怀揣着开源梦想的程序员披荆斩棘、不断前行,不夸张的说,没有你们,开源世界就没有星光,自然也就不会有今天的开源盛世。
因此,**我恳请大家,如果觉得书还可以,就在你的指尖星空绘制一颗新的 🌟,指引我们继续砥砺前行**。这个人世间,因善意而美好。
最后,能通过开源在茫茫人海中与大家相识,这感觉真好 :D
## 🏅 贡献者 ## 🏅 贡献者
非常感谢本教程的[所有贡献者](https://github.com/sunface/rust-course/graphs/contributors),正是有了你们,才有了现在的高质量 Rust 教程! 非常感谢本教程的[所有贡献者](https://github.com/sunface/rust-course/graphs/contributors),正是有了你们,才有了现在的高质量 Rust 教程!
@ -99,4 +116,4 @@ Rust语言真的好连续六年成为全世界最受欢迎的语言、没有G
</a> </a>
</td> </td>
</tr> </tr>
</table> </table>

@ -303,7 +303,7 @@ where
} }
``` ```
上面的缓存有一个很大的问题:只支持 `u32` 类型的值,若我们想要缓存 `String` 类型,显然就行不通了,因此需要将 `u32` 替换成泛型 `E`,该练习就留给读者自己完成,具体代码可以参考[这里](http://exercise.rs/functional-programming/closure.html) 上面的缓存有一个很大的问题:只支持 `u32` 类型的值,若我们想要缓存 `String` 类型,显然就行不通了,因此需要将 `u32` 替换成泛型 `E`,该练习就留给读者自己完成,具体代码可以参考[这里](https://practice.rs/functional-programing/cloure.html#closure-in-structs)
## 捕获作用域中的值 ## 捕获作用域中的值
@ -767,3 +767,6 @@ fn factory(x:i32) -> Box<dyn Fn(i32) -> i32> {
## 闭包的生命周期 ## 闭包的生命周期
这块儿内容在进阶生命周期章节中有讲,这里就不再赘述,读者可移步[此处](https://course.rs/advance/lifetime/advance.html#闭包函数的消除规则)进行回顾。 这块儿内容在进阶生命周期章节中有讲,这里就不再赘述,读者可移步[此处](https://course.rs/advance/lifetime/advance.html#闭包函数的消除规则)进行回顾。
{{#include ../../practice.md}}

@ -311,7 +311,7 @@ hello_macro
由于过程宏所在的包跟我们的项目紧密相连,因此将它放在项目之中。现在,问题又来了,该如何在项目的 `src/main.rs` 中引用 `hello_macro_derive` 包的内容? 由于过程宏所在的包跟我们的项目紧密相连,因此将它放在项目之中。现在,问题又来了,该如何在项目的 `src/main.rs` 中引用 `hello_macro_derive` 包的内容?
方法有两种,第一种是将 `hello_macro_derive` 发布到 `crates.io``github` 中,就像我们引用的其它依赖一样;另一种就是使用相对路径引入的本地化方式,修改 `hello_macro/Cargo.toml` 文件添加以下内容: 方法有两种,第一种是将 `hello_macro_derive` 发布到 `crates.io``GitHub` 中,就像我们引用的其它依赖一样;另一种就是使用相对路径引入的本地化方式,修改 `hello_macro/Cargo.toml` 文件添加以下内容:
```toml ```toml
[dependencies] [dependencies]

@ -105,6 +105,6 @@ error: a bin target must be available for `cargo run`
- 基准性能测试 `benchmark` 文件:`benches` 目录下 - 基准性能测试 `benchmark` 文件:`benches` 目录下
- 项目示例:`examples` 目录下 - 项目示例:`examples` 目录下
这种目录结构基本上是 Rust 的标准目录结构,在 `github` 的大多数项目上,你都将看到它的身影。 这种目录结构基本上是 Rust 的标准目录结构,在 `GitHub` 的大多数项目上,你都将看到它的身影。
理解了包的概念,我们再来看看构成包的基本单元:模块。 理解了包的概念,我们再来看看构成包的基本单元:模块。

@ -261,7 +261,7 @@ mod back_of_house {
## 使用 `self` 引用模块 ## 使用 `self` 引用模块
`self` 其实就是引用自身模块中的项,也就是说和我们之前章节的代码类似,都调用同一模块中的内容,区别在于之章节中直接通过名称调用即可,而 `self`,你得多此一举: `self` 其实就是引用自身模块中的项,也就是说和我们之前章节的代码类似,都调用同一模块中的内容,区别在于之章节中直接通过名称调用即可,而 `self`,你得多此一举:
```rust ```rust
fn serve_order() { fn serve_order() {

@ -9,7 +9,7 @@
## 是否上传本地的 `Cargo.lock` ## 是否上传本地的 `Cargo.lock`
当本地开发时,`Cargo.lock` 自然是非常重要的,但是当你要把项目上传到 `Git` 时,例如 `Github`,那是否上传 `Cargo.lock` 就成了一个问题。 当本地开发时,`Cargo.lock` 自然是非常重要的,但是当你要把项目上传到 `Git` 时,例如 `GitHub`,那是否上传 `Cargo.lock` 就成了一个问题。
关于是否上传,有如下经验准则: 关于是否上传,有如下经验准则:
@ -39,7 +39,7 @@ version = "0.1.0"
regex = { git = "https://github.com/rust-lang/regex.git" } regex = { git = "https://github.com/rust-lang/regex.git" }
``` ```
可以看到,只有一个依赖,且该依赖的来源是 `Github` 上一个特定的仓库。由于我们没有指定任何版本信息,`Cargo` 会自动拉取该依赖库的最新版本( `master``main` 分支上的最新 `commit` )。 可以看到,只有一个依赖,且该依赖的来源是 `GitHub` 上一个特定的仓库。由于我们没有指定任何版本信息,`Cargo` 会自动拉取该依赖库的最新版本( `master``main` 分支上的最新 `commit` )。
这种使用方式,其实就错失了包管理工具的最大的优点:版本管理。例如你在今天构建使用了版本 `A`,然后过了一段时间后,由于依赖包的升级,新的构建却使用了大更新版本 `B`,结果因为版本不兼容,导致了构建失败。 这种使用方式,其实就错失了包管理工具的最大的优点:版本管理。例如你在今天构建使用了版本 `A`,然后过了一段时间后,由于依赖包的升级,新的构建却使用了大更新版本 `B`,结果因为版本不兼容,导致了构建失败。

@ -1,13 +1,13 @@
# 下载并构建 Package # 下载并构建 Package
如果看中 `Github` 上的某个开源 Rust 项目,那下载并构建它将是非常简单的。 如果看中 `GitHub` 上的某个开源 Rust 项目,那下载并构建它将是非常简单的。
```shell ```shell
$ git clone https://github.com/rust-lang/regex.git $ git clone https://github.com/rust-lang/regex.git
$ cd regex $ cd regex
``` ```
如上所示,直接从 `github` 上克隆下来想要的项目,然后使用 `cargo build` 进行构建即可: 如上所示,直接从 `GitHub` 上克隆下来想要的项目,然后使用 `cargo build` 进行构建即可:
```shell ```shell
$ cargo build $ cargo build

@ -27,9 +27,9 @@ test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
在有了持续集成后,只要编写好相应的编译、测试、发布配置文件,那持续集成平台会自动帮助我们完成整个相关的流程,期间无需任何人介入,高效且可靠。 在有了持续集成后,只要编写好相应的编译、测试、发布配置文件,那持续集成平台会自动帮助我们完成整个相关的流程,期间无需任何人介入,高效且可靠。
#### Github Actions #### GitHub Actions
关于如何使用 `Github Actions` 进行持续集成,在[之前的章节](https://course.rs/test/ci.html)已经有过详细的介绍,这里就不再赘述。 关于如何使用 `GitHub Actions` 进行持续集成,在[之前的章节](https://course.rs/test/ci.html)已经有过详细的介绍,这里就不再赘述。
#### Travis CI #### Travis CI

@ -32,7 +32,7 @@ fn main() {
> Note: [`package.build`](https://course.rs/cargo/reference/manifest.html#build) 可以用于改变构建脚本的名称,或者直接禁用该功能 > Note: [`package.build`](https://course.rs/cargo/reference/manifest.html#build) 可以用于改变构建脚本的名称,或者直接禁用该功能
#### 构建脚本的生命期 #### 构建脚本的生命
在项目被构建之前Cargo 会将构建脚本编译成一个可执行文件,然后运行该文件并执行相应的任务。 在项目被构建之前Cargo 会将构建脚本编译成一个可执行文件,然后运行该文件并执行相应的任务。
@ -40,7 +40,7 @@ fn main() {
需要注意的是Cargo 也不是每次都会重新编译构建脚本,只有当脚本的内容或依赖发生变化时才会。默认情况下,任何文件变化都会触发重新编译,如果你希望对其进行定制,可以使用 `rerun-if`命令,后文会讲。 需要注意的是Cargo 也不是每次都会重新编译构建脚本,只有当脚本的内容或依赖发生变化时才会。默认情况下,任何文件变化都会触发重新编译,如果你希望对其进行定制,可以使用 `rerun-if`命令,后文会讲。
在构建本成功执行后,我们的项目就会开始进行编译。如果构建脚本的运行过程中发生错误,脚本应该通过返回一个非 0 码来立刻退出,在这种情况下,构建脚本的输出会被打印到终端中。 在构建本成功执行后,我们的项目就会开始进行编译。如果构建脚本的运行过程中发生错误,脚本应该通过返回一个非 0 码来立刻退出,在这种情况下,构建脚本的输出会被打印到终端中。
#### 构建脚本的输入 #### 构建脚本的输入
@ -72,8 +72,8 @@ $ cargo run -vv
以下是 Cargo 能识别的通信指令以及简介,如果大家希望深入了解每个命令,可以点击具体的链接查看官方文档的说明。 以下是 Cargo 能识别的通信指令以及简介,如果大家希望深入了解每个命令,可以点击具体的链接查看官方文档的说明。
- [`cargo:rerun-if-changed=PATH`](https://doc.rust-lang.org/stable/cargo/reference/build-scripts.html#rerun-if-changed) — 当指定路径的文件发生变化时Cargo 会重新运行脚本 - [`cargo:rerun-if-changed=PATH`](https://doc.rust-lang.org/stable/cargo/reference/build-scripts.html#rerun-if-changed) — 当指定路径的文件发生变化时Cargo 会重新运行脚本
- [`cargo:rerun-if-env-changed=VAR`](https://doc.rust-lang.org/stable/cargo/reference/build-scripts.html#rerun-if-env-changed) — 当指定的环境变量发生变化时Cargo 会重新运行脚本告诉 - [`cargo:rerun-if-env-changed=VAR`](https://doc.rust-lang.org/stable/cargo/reference/build-scripts.html#rerun-if-env-changed) — 当指定的环境变量发生变化时Cargo 会重新运行脚本
- [`cargo:rustc-link-arg=FLAG`](https://doc.rust-lang.org/stable/cargo/reference/build-scripts.html#rustc-link-arg) 将自定义的 flags 传给 linker用于后续的基准性能测试 benchmark、 可执行文件 binary,、`cdylib` 包、示例 和测试 - [`cargo:rustc-link-arg=FLAG`](https://doc.rust-lang.org/stable/cargo/reference/build-scripts.html#rustc-link-arg) 将自定义的 flags 传给 linker用于后续的基准性能测试 benchmark、 可执行文件 binary,、`cdylib` 包、示例和测试
- [`cargo:rustc-link-arg-bin=BIN=FLAG`](https://doc.rust-lang.org/stable/cargo/reference/build-scripts.html#rustc-link-arg-bin) 自定义的 flags 传给 linker用于可执行文件 `BIN` - [`cargo:rustc-link-arg-bin=BIN=FLAG`](https://doc.rust-lang.org/stable/cargo/reference/build-scripts.html#rustc-link-arg-bin) 自定义的 flags 传给 linker用于可执行文件 `BIN`
- [`cargo:rustc-link-arg-bins=FLAG`](https://doc.rust-lang.org/stable/cargo/reference/build-scripts.html#rustc-link-arg-bins) 自定义的 flags 传给 linker用于可执行文件 - [`cargo:rustc-link-arg-bins=FLAG`](https://doc.rust-lang.org/stable/cargo/reference/build-scripts.html#rustc-link-arg-bins) 自定义的 flags 传给 linker用于可执行文件
- [`cargo:rustc-link-arg-tests=FLAG`](https://doc.rust-lang.org/stable/cargo/reference/build-scripts.html#rustc-link-arg-tests) 自定义的 flags 传给 linker用于测试 - [`cargo:rustc-link-arg-tests=FLAG`](https://doc.rust-lang.org/stable/cargo/reference/build-scripts.html#rustc-link-arg-tests) 自定义的 flags 传给 linker用于测试
@ -90,7 +90,7 @@ $ cargo run -vv
## 构建脚本的依赖 ## 构建脚本的依赖
构建脚本也可以引入其它基于 Cargo 的依赖包,只需要修改`Cargo.toml` 中添加以下内容: 构建脚本也可以引入其它基于 Cargo 的依赖包,只需要在 `Cargo.toml` 中添加或修改以下内容:
```toml ```toml
[build-dependencies] [build-dependencies]
@ -115,7 +115,7 @@ links = "foo"
Cargo 要求一个本地库最多只能被一个项目所链接,换而言之,你无法让两个项目链接到同一个本地库,但是有一种方法可以降低这种限制,感兴趣的同学可以看看[官方文档](https://doc.rust-lang.org/stable/cargo/reference/build-scripts.html#-sys-packages)。 Cargo 要求一个本地库最多只能被一个项目所链接,换而言之,你无法让两个项目链接到同一个本地库,但是有一种方法可以降低这种限制,感兴趣的同学可以看看[官方文档](https://doc.rust-lang.org/stable/cargo/reference/build-scripts.html#-sys-packages)。
假设 A 项目的构建脚本生成任意数量的 kv 形式的元数据,那这些元数据传递给 A 用作依赖包的项目的构建脚本。例如,如果包 `bar` 依赖于 `foo`,当 `foo` 生成 `key==value` 形式的构建脚本元数据时,那么 `bar` 的构建脚本就可以通过环境变量的形式使用该元数据:`DEP_FOO_KEY=value`。 假设 A 项目的构建脚本生成任意数量的 kv 形式的元数据,那这些元数据传递给 A 用作依赖包的项目的构建脚本。例如,如果包 `bar` 依赖于 `foo`,当 `foo` 生成 `key=value` 形式的构建脚本元数据时,那么 `bar` 的构建脚本就可以通过环境变量的形式使用该元数据:`DEP_FOO_KEY=value`。
需要注意的是,该元数据只能传给直接相关者,对于间接的,例如依赖的依赖,就无能为力了。 需要注意的是,该元数据只能传给直接相关者,对于间接的,例如依赖的依赖,就无能为力了。

@ -108,7 +108,7 @@ uuid = { git = 'https://github.com/uuid-rs/uuid' }
#### 间接使用 `patch` #### 间接使用 `patch`
现在假设项目 `A` 的依赖是 `B``uuid`,而 `B` 的依赖也是 `uuid`,此时我们可以让 `A``B` 都使用来自 `github` 的 `patch` 版本,配置如下: 现在假设项目 `A` 的依赖是 `B``uuid`,而 `B` 的依赖也是 `uuid`,此时我们可以让 `A``B` 都使用来自 `GitHub` 的 `patch` 版本,配置如下:
```toml ```toml
[package] [package]

@ -12,7 +12,7 @@
### 行为扩展 ### 行为扩展
[`serde_json`](https://crates.io/crates/serde_json) 拥有一个 [`preserve_order` feature](https://github.com/serde-rs/json/blob/v1.0.60/Cargo.toml#L53-L56),可以用于在序列化时保留 JSON 键值的顺序。同时,该 feature 还会启用一个可选依赖 [indexmap](https://crates.io/crates/indexmap)。 [`serde_json`](https://crates.io/crates/serde_json) 拥有一个 [`preserve_order` feature](https://github.com/serde-rs/json/blob/v1.0.60/Cargo.toml#L53-L56),可以用于在序列化时保留 JSON 键值的顺序。同时,该 feature 还会启用一个可选依赖 [indexmap](https://crates.io/crates/indexmap)。
当这么做时,一定要小心不要破坏了 SemVer 的版本兼容性,也就是说:启用 feature 后,代码依然要能正常工作。 当这么做时,一定要小心不要破坏了 SemVer 的版本兼容性,也就是说:启用 feature 后,代码依然要能正常工作。
@ -40,7 +40,7 @@
在这种情况下,将过程宏所在的包定义为可选依赖,是很不错的选择。这样做还有一个好处:有时过程宏的版本必须要跟父包进行同步,但是我们又不希望所有的用户都进行同步。 在这种情况下,将过程宏所在的包定义为可选依赖,是很不错的选择。这样做还有一个好处:有时过程宏的版本必须要跟父包进行同步,但是我们又不希望所有的用户都进行同步。
其中一个例子就是 [serde](https://crates.io/crates/serde) ,它有一个 [derive](https://github.com/serde-rs/serde/blob/v1.0.118/serde/Cargo.toml#L34-L35) feature 可以启用 [serde_derive](https://crates.io/crates/serde_derive) 过程宏。由于 `serde_derive` 包跟 `serde` 的关系非常紧密,因此它使用了[版本相同的需求](https://github.com/serde-rs/serde/blob/v1.0.118/serde/Cargo.toml#L17)来保证两者的版本同步性。 其中一个例子就是 [serde](https://crates.io/crates/serde) ,它有一个 [derive](https://github.com/serde-rs/serde/blob/v1.0.118/serde/Cargo.toml#L34-L35) feature 可以启用 [serde_derive](https://crates.io/crates/serde_derive) 过程宏。由于 `serde_derive` 包跟 `serde` 的关系非常紧密,因此它使用了[版本相同的需求](https://github.com/serde-rs/serde/blob/v1.0.118/serde/Cargo.toml#L17)来保证两者的版本同步性。
## 只能用于 nightly 的 feature ## 只能用于 nightly 的 feature
@ -52,7 +52,7 @@ Rust 有些实验性的 API 或语言特性只能在 nightly 版本下使用,
## 实验性 feature ## 实验性 feature
有一些包会提前将一些实验性的 API 放出去,既然是实验性的,自然无法保证其稳定性。在这种情况下,通会在文档中将相应的 features 标记为实验性,意味着它们在未来可能会发生大的改变(甚至 minor 版本都可能发生)。 有一些包会提前将一些实验性的 API 放出去,既然是实验性的,自然无法保证其稳定性。在这种情况下,通会在文档中将相应的 features 标记为实验性,意味着它们在未来可能会发生大的改变(甚至 minor 版本都可能发生)。
其中一个例子是 [async-std](https://crates.io/crates/async-std) 包,它拥有一个 [unstable feature](https://github.com/async-rs/async-std/blob/v1.8.0/Cargo.toml#L38-L42),用来[标记一些新的 API](https://github.com/async-rs/async-std/blob/v1.8.0/src/macros.rs#L46),表示人们已经可以选择性的使用但是还没有准备好去依赖它。 其中一个例子是 [async-std](https://crates.io/crates/async-std) 包,它拥有一个 [unstable feature](https://github.com/async-rs/async-std/blob/v1.8.0/Cargo.toml#L38-L42),用来[标记一些新的 API](https://github.com/async-rs/async-std/blob/v1.8.0/src/macros.rs#L46),表示人们已经可以选择性的使用但是还没有准备好去依赖它。

@ -25,7 +25,7 @@ pub mod webp;
`Cargo.toml` 中定义的 `feature` 会被 `Cargo` 通过命令行参数 `--cfg` 传给 `rustc`,最终由后者完成编译:`rustc --cfg ...`。若项目中的代码想要测试 `feature` 是否存在,可以使用 [`cfg` 属性](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#the-cfg-attribute)或 [`cfg` 宏](https://doc.rust-lang.org/stable/std/macro.cfg.html)。 `Cargo.toml` 中定义的 `feature` 会被 `Cargo` 通过命令行参数 `--cfg` 传给 `rustc`,最终由后者完成编译:`rustc --cfg ...`。若项目中的代码想要测试 `feature` 是否存在,可以使用 [`cfg` 属性](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#the-cfg-attribute)或 [`cfg` 宏](https://doc.rust-lang.org/stable/std/macro.cfg.html)。
之前我们提到了一个 `feature` 还可以开启其他 `feature`,举个例子,例如 `ICO` 图片格式包含 `BMP``PNG`,因此当 `ICO` 图片格式被启用后,它还得确保启用 `BMP``PNG` 格式 之前我们提到了一个 `feature` 还可以开启其他 `feature`,举个例子,例如 ICO 图片格式包含 BMP 和 PNG 格式,因此当 `ico` 被启用后,它还得确保启用 `bmp``png`
```toml ```toml
[features] [features]
@ -70,11 +70,11 @@ webp = []
gif = { version = "0.11.1", optional = true } gif = { version = "0.11.1", optional = true }
``` ```
**这种可选依赖的写法会自动定义一个与依赖同名的 feature也就是 `gif` feature**,这样一来,当我们启用 `gif` feautre 时,该依赖库也会被自动引入并启用:例如通过 `--feature gif` 的方式启用 feauture。 **这种可选依赖的写法会自动定义一个与依赖同名的 feature也就是 `gif` feature**,这样一来,当我们启用 `gif` feature 时,该依赖库也会被自动引入并启用:例如通过 `--feature gif` 的方式启用 feature
> 注意:目前来说,`[fetuare]` 中定义的 feature 还不能与已引入的依赖库同名。但是在 `nightly` 中已经提供了实验性的功能用于改变这一点: [namespaced features](https://doc.rust-lang.org/stable/cargo/reference/unstable.html#namespaced-features) > 注意:目前来说,`[feature]` 中定义的 feature 还不能与已引入的依赖库同名。但是在 `nightly` 中已经提供了实验性的功能用于改变这一点: [namespaced features](https://doc.rust-lang.org/stable/cargo/reference/unstable.html#namespaced-features)
当然,**我们还可以通过显式定义 feature 的方式来启用这些可选依赖库**,例如为了支持 `AVIF` 图片格式,我们需要引入两个依赖包,由于 `AVIF` 是通过 feature 引入的可选格式,因此它依赖的两个包也必须声明为可选的: 当然,**我们还可以通过显式定义 feature 的方式来启用这些可选依赖库**,例如为了支持 AVIF 图片格式,我们需要引入两个依赖包,由于 `avif` 是通过 feature 引入的可选格式,因此它依赖的两个包也必须声明为可选的:
```toml ```toml
[dependencies] [dependencies]
@ -91,7 +91,7 @@ avif = ["ravif", "rgb"]
## 依赖库自身的 feature ## 依赖库自身的 feature
就像我们的项目可以定义 `feature` 一样,依赖库也可以定义它自己的 feature、也有需要启用的 feature 列表,当引入该依赖库时,我们可以通过以下方式为其启用相关的 features : 就像我们的项目可以定义 `feature` 一样,依赖库也可以定义它自己的 `feature`,也有需要启用的 `feature` 列表,当引入该依赖库时,我们可以通过以下方式为其启用相关的 `features` :
```toml ```toml
[dependencies] [dependencies]
@ -109,7 +109,7 @@ flate2 = { version = "1.0.3", default-features = false, features = ["zlib"] }
> 注意:这种方式未必能成功禁用 `default`,原因是可能会有其它依赖也引入了 `flate2`,并且没有对 `default` 进行禁用,那此时 `default` 依然会被启用。 > 注意:这种方式未必能成功禁用 `default`,原因是可能会有其它依赖也引入了 `flate2`,并且没有对 `default` 进行禁用,那此时 `default` 依然会被启用。
> >
> 查看下文的 [feature 同一化](#feature同一化) 获取更多信息 > 查看下文的 [feature 同一化](#feature-同一化) 获取更多信息
除此之外,还能通过下面的方式来间接开启依赖库的 feature : 除此之外,还能通过下面的方式来间接开启依赖库的 feature :
@ -134,7 +134,7 @@ parallel = ["jpeg-decoder/rayon"]
- `--features FEATURES`: 启用给出的 feature 列表,可以使用逗号或空格进行分隔,若你是在终端中使用,还需要加上双引号,例如 `--features "foo bar"`。 若在工作空间中构建多个 `package`,可以使用 `package-name/feature-name` 为特定的成员启用 features - `--features FEATURES`: 启用给出的 feature 列表,可以使用逗号或空格进行分隔,若你是在终端中使用,还需要加上双引号,例如 `--features "foo bar"`。 若在工作空间中构建多个 `package`,可以使用 `package-name/feature-name` 为特定的成员启用 features
- `--all-features`: 启用命令行上所选择的所有包的所有 features - `--all-features`: 启用命令行上所选择的所有包的所有 features
- `--no-default-features`: 对选择的包禁用 `default` featue - `--no-default-features`: 对选择的包禁用 `default` feature
## feature 同一化 ## feature 同一化
@ -150,7 +150,7 @@ parallel = ["jpeg-decoder/rayon"]
例如,如果我们想可选的支持 `no_std` 环境(不使用标准库),那么有两种做法: 例如,如果我们想可选的支持 `no_std` 环境(不使用标准库),那么有两种做法:
- 默认代码使用标准库的,当 `no_std` feature 启用时,禁用相关的标准库代码 - 默认代码使用标准库的,当 `no_std` feature 启用时,禁用相关的标准库代码
- 默认代码使用非标准库的,当 `std` feature 启用时,才使用标准库的代码 - 默认代码使用非标准库的,当 `std` feature 启用时,才使用标准库的代码
前者就是功能削减,与之相对,后者是功能添加,根据之前的内容,我们应该选择后者的做法: 前者就是功能削减,与之相对,后者是功能添加,根据之前的内容,我们应该选择后者的做法:
@ -203,7 +203,7 @@ test_cargo v0.1.0 (/Users/sunfei/development/rust/demos/test_cargo)
`cargo tree -f "{p} {f}"` 命令会提供一个更加紧凑的视图: `cargo tree -f "{p} {f}"` 命令会提供一个更加紧凑的视图:
```shell ```shell
% cargo tree -f "{p} {f}" $ cargo tree -f "{p} {f}"
test_cargo v0.1.0 (/Users/sunfei/development/rust/demos/test_cargo) test_cargo v0.1.0 (/Users/sunfei/development/rust/demos/test_cargo)
└── uuid v0.8.2 default,std └── uuid v0.8.2 default,std
``` ```
@ -211,7 +211,7 @@ test_cargo v0.1.0 (/Users/sunfei/development/rust/demos/test_cargo)
`cargo tree -e features -i foo`,该命令会显示 `features` 会如何"流入"指定的包 `foo` 中: `cargo tree -e features -i foo`,该命令会显示 `features` 会如何"流入"指定的包 `foo` 中:
```shell ```shell
cargo tree -e features -i uuid $ cargo tree -e features -i uuid
uuid v0.8.2 uuid v0.8.2
├── uuid feature "default" ├── uuid feature "default"
│ └── test_cargo v0.1.0 (/Users/sunfei/development/rust/demos/test_cargo) │ └── test_cargo v0.1.0 (/Users/sunfei/development/rust/demos/test_cargo)
@ -238,8 +238,8 @@ resolver = "2"
V2 版本的解析器可以在某些情况下避免 feature 同一化的发生,具体的情况在[这里](https://doc.rust-lang.org/stable/cargo/reference/resolver.html#feature-resolver-version-2)有描述,下面做下简单的总结: V2 版本的解析器可以在某些情况下避免 feature 同一化的发生,具体的情况在[这里](https://doc.rust-lang.org/stable/cargo/reference/resolver.html#feature-resolver-version-2)有描述,下面做下简单的总结:
- 为特定平台开启的 `features` 且此时并没有被构建,会被忽略 - 为特定平台开启的 `features` 且此时并没有被构建,会被忽略
- `Build-dependencies` 和 `proc-macros` 不再跟普通的依赖共享 `features` - `build-dependencies` 和 `proc-macros` 不再跟普通的依赖共享 `features`
- `Dev-dependencies` 的 `features` 不会被启用,除非正在构建的对象需要它们(例如测试对象、示例对象等) - `dev-dependencies` 的 `features` 不会被启用,除非正在构建的对象需要它们(例如测试对象、示例对象等)
对于部分场景而言feature 同一化确实是需要避免的,例如,一个构建依赖开启了 `std` feature而同一个依赖又被用于 `no_std` 环境,很明显,开启 `std` 将导致错误的发生。 对于部分场景而言feature 同一化确实是需要避免的,例如,一个构建依赖开启了 `std` feature而同一个依赖又被用于 `no_std` 环境,很明显,开启 `std` 将导致错误的发生。
@ -249,7 +249,7 @@ V2 版本的解析器可以在某些情况下避免 feature 同一化的发生
## 构建脚本 ## 构建脚本
[构建脚本](https://course.rs/cargo/reference/build-script/intro.html)可以通过 `CARGO_FEATURE_<name>` 环境变量获取启用的 `feauture` 列表,其中 `<name>` 是 feature 的名称,该名称被转换成大全写字母,且 `-` 被转换为 `_` [构建脚本](https://course.rs/cargo/reference/build-script/intro.html)可以通过 `CARGO_FEATURE_<name>` 环境变量获取启用的 `feature` 列表,其中 `<name>` 是 feature 的名称,该名称被转换成大全写字母,且 `-` 被转换为 `_`
## required-features ## required-features
@ -257,14 +257,14 @@ V2 版本的解析器可以在某些情况下避免 feature 同一化的发生
## SemVer 兼容性 ## SemVer 兼容性
启用一个 feautre 不应该引入一个不兼容 SemVer 的改变。例如,启用的 feature 不应该改变现有的 API因为这会给用户造成不兼容的破坏性变更。 如果大家想知道哪些变化是兼容的,可以参见[官方文档](https://doc.rust-lang.org/stable/cargo/reference/semver.html)。 启用一个 feature 不应该引入一个不兼容 SemVer 的改变。例如,启用的 feature 不应该改变现有的 API因为这会给用户造成不兼容的破坏性变更。 如果大家想知道哪些变化是兼容的,可以参见[官方文档](https://doc.rust-lang.org/stable/cargo/reference/semver.html)。
总之,在新增/移除 feature 或可选依赖时,你需要小心,因此这些可能会造成向后不兼容性。更多信息参见[这里](https://doc.rust-lang.org/stable/cargo/reference/semver.html#cargo),简单总结如下: 总之,在新增/移除 feature 或可选依赖时,你需要小心,因此这些可能会造成向后不兼容性。更多信息参见[这里](https://doc.rust-lang.org/stable/cargo/reference/semver.html#cargo),简单总结如下:
- 在发布 `minor` 版本时,以下通常是安全的: - 在发布 `minor` 版本时,以下通常是安全的:
- [新增 feature](https://doc.rust-lang.org/stable/cargo/reference/semver.html#cargo-feature-add) 或[可选依赖](https://doc.rust-lang.org/stable/cargo/reference/semver.html#cargo-dep-add) - [新增 feature](https://doc.rust-lang.org/stable/cargo/reference/semver.html#cargo-feature-add) 或[可选依赖](https://doc.rust-lang.org/stable/cargo/reference/semver.html#cargo-dep-add)
- [修改某个依赖的 features](https://doc.rust-lang.org/stable/cargo/reference/semver.html#cargo-change-dep-feature) - [修改某个依赖的 features](https://doc.rust-lang.org/stable/cargo/reference/semver.html#cargo-change-dep-feature)
- 在发布 `minor` 时,以下操作应该避免: - 在发布 `minor` 版本时,以下操作应该避免:
- [移除 feature](https://doc.rust-lang.org/stable/cargo/reference/semver.html#cargo-feature-remove) 或[可选依赖](https://doc.rust-lang.org/stable/cargo/reference/semver.html#cargo-remove-opt-dep) - [移除 feature](https://doc.rust-lang.org/stable/cargo/reference/semver.html#cargo-feature-remove) 或[可选依赖](https://doc.rust-lang.org/stable/cargo/reference/semver.html#cargo-remove-opt-dep)
- [将现有的公有代码放在某个 feature 之后](https://doc.rust-lang.org/stable/cargo/reference/semver.html#cargo-remove-opt-dep) - [将现有的公有代码放在某个 feature 之后](https://doc.rust-lang.org/stable/cargo/reference/semver.html#cargo-remove-opt-dep)
- [从 feature 列表中移除一个 feature](https://doc.rust-lang.org/stable/cargo/reference/semver.html#cargo-feature-remove-another) - [从 feature 列表中移除一个 feature](https://doc.rust-lang.org/stable/cargo/reference/semver.html#cargo-feature-remove-another)

@ -184,7 +184,7 @@ homepage = "https://serde.rs/"
#### repository #### repository
设置项目的源代码仓库地址,例如 `github` 链接: 设置项目的源代码仓库地址,例如 `GitHub` 链接:
```toml ```toml
[package] [package]

@ -25,7 +25,7 @@ overflow-checks = false # 关闭整数溢出检查
需要注意的是,每一种 profile 都可以单独的进行设置,例如上面的 `[profile.dev]` 需要注意的是,每一种 profile 都可以单独的进行设置,例如上面的 `[profile.dev]`
如果是工作空间的话,只有根 package 的 `Cargo.toml` 中的 `[profile` 设置才会被使用,其它成员或依赖包中的设置会被自动忽略。 如果是工作空间的话,只有根 package 的 `Cargo.toml` 中的 `[profile]` 设置才会被使用,其它成员或依赖包中的设置会被自动忽略。
另外profile 还能在 Cargo 自身的配置文件中进行覆盖,总之,通过 `.cargo/config.toml` 或环境变量的方式所指定的 `profile` 配置会覆盖项目的 `Cargo.toml` 中相应的配置。 另外profile 还能在 Cargo 自身的配置文件中进行覆盖,总之,通过 `.cargo/config.toml` 或环境变量的方式所指定的 `profile` 配置会覆盖项目的 `Cargo.toml` 中相应的配置。
@ -126,9 +126,9 @@ cargo build --profile release-lto
支持的选项包括: 支持的选项包括:
- `false`: 只会对代码生成单元中的本地包进行 `thin LTO` 优化,若代码生成单元数为 1 或者 `opt-level` 为 0则不会进行任何 LTO 优化 - `false`: 只会对代码生成单元中的本地包进行 `"thin" LTO` 优化,若代码生成单元数为 1 或者 `opt-level` 为 0则不会进行任何 LTO 优化
- `true``fat`:对依赖图中的所有包进行 `fat LTO` 优化 - `true``"fat"`:对依赖图中的所有包进行 `"fat" LTO` 优化
- `thin`:对依赖图的所有包进行 [`thin LTO`](http://blog.llvm.org/2016/06/thinlto-scalable-and-incremental-lto.html),相比 `fat` 来说,它仅牺牲了一点性能,但是换来了链接时间的可观减少 - `"thin"`:对依赖图的所有包进行 [`"thin" LTO`](http://blog.llvm.org/2016/06/thinlto-scalable-and-incremental-lto.html),相比 `"fat"` 来说,它仅牺牲了一点性能,但是换来了链接时间的可观减少
- `off` 禁用 LTO - `off` 禁用 LTO
如果大家想了解跨语言 LTO可以看下 [-C linker-plugin-lto](https://doc.rust-lang.org/stable/rustc/codegen-options/index.html#linker-plugin-lto) 标志。 如果大家想了解跨语言 LTO可以看下 [-C linker-plugin-lto](https://doc.rust-lang.org/stable/rustc/codegen-options/index.html#linker-plugin-lto) 标志。

@ -1,12 +1,12 @@
# 发布到 crates.io # 发布到 crates.io
如果你想要把自己的开源项目分享给全世界,那最好的办法自然是 github。但如果是 Rust 的库,那除了发布到 github 外,我们还可以将其发布到 [crates.io](https://crates.io) 上,然后其它用户就可以很简单的对其进行引用。 如果你想要把自己的开源项目分享给全世界,那最好的办法自然是 GitHub。但如果是 Rust 的库,那除了发布到 GitHub 外,我们还可以将其发布到 [crates.io](https://crates.io) 上,然后其它用户就可以很简单的对其进行引用。
> 注意:发布包到 `crates.io` 后,特定的版本无法被覆盖,要发布就必须使用新的版本号,代码也无法被删除! > 注意:发布包到 `crates.io` 后,特定的版本无法被覆盖,要发布就必须使用新的版本号,代码也无法被删除!
## 首次发布之前 ## 首次发布之前
**首先,我们需要一个账号**:访问 crates.io 的[主页](https://crates.io),然后在右上角使用 Github 账户登陆,接着访问你的[账户设置](https://crates.io/settings/profile)页面,进入到 API Tokens 标签页下,生成新的 Token并使用该 Token 在终端中进行登录: **首先,我们需要一个账号**:访问 crates.io 的[主页](https://crates.io),然后在右上角使用 GitHub 账户登陆,接着访问你的[账户设置](https://crates.io/settings/profile)页面,进入到 API Tokens 标签页下,生成新的 Token并使用该 Token 在终端中进行登录:
```shell ```shell
$ cargo login abcdefghijklmnopqrstuvwxyz012345 $ cargo login abcdefghijklmnopqrstuvwxyz012345
@ -52,7 +52,7 @@ $ cargo publish --dry-run
你可以在 `target/package` 目录下观察生成的 `.crate` 文件。例如,目前 `crates.io` 要求该文件的大小不能超过 10MB你可以通过手动检查该文件的大小来确保不会无意间打包进一些较大的资源文件比如测试数据、网站文档或生成的代码等。我们还可以使用以下命令来检查其中包含的文件: 你可以在 `target/package` 目录下观察生成的 `.crate` 文件。例如,目前 `crates.io` 要求该文件的大小不能超过 10MB你可以通过手动检查该文件的大小来确保不会无意间打包进一些较大的资源文件比如测试数据、网站文档或生成的代码等。我们还可以使用以下命令来检查其中包含的文件:
```shell ```shell
$cargo package --list $ cargo package --list
``` ```
当打包时Cargo 会自动根据版本控制系统的配置来忽略指定的文件,例如 `.gitignore`。除此之外,你还可以通过 [`exclude`](https://course.rs/cargo/reference/manifest.html#exclude和include) 来排除指定的文件: 当打包时Cargo 会自动根据版本控制系统的配置来忽略指定的文件,例如 `.gitignore`。除此之外,你还可以通过 [`exclude`](https://course.rs/cargo/reference/manifest.html#exclude和include) 来排除指定的文件:
@ -123,12 +123,12 @@ $ cargo owner --add github:rust-lang:owners
$ cargo owner --remove github:rust-lang:owners $ cargo owner --remove github:rust-lang:owners
``` ```
命令中使用的 ownerID 必须是 Github 用户名或 Team 名。 命令中使用的 ownerID 必须是 GitHub 用户名或 Team 名。
一旦一个用户 `B` 通过 `--add` 被加入到 `owner` 列表中他将拥有该包相关的所有权利。例如发布新版本、yank 一个版本,还能增加和移除 owner包含添加 `B` 为 owner 的 `A` 都可以被移除! 一旦一个用户 `B` 通过 `--add` 被加入到 `owner` 列表中他将拥有该包相关的所有权利。例如发布新版本、yank 一个版本,还能增加和移除 owner包含添加 `B` 为 owner 的 `A` 都可以被移除!
因此,我们必须严肃的指出:**不要将你不信任的人添加为 owner !** 免得哪天反目成仇后,他把你移除了 - , - 因此,我们必须严肃的指出:**不要将你不信任的人添加为 owner !** 免得哪天反目成仇后,他把你移除了 - , -
但是对于 Team 又有所不同,通过 `-add` 添加的 Github Team owner只拥有受限的权利。它们可以发布或 yank 某个版本,但是他们**不能添加或移除** owner总之Team 除了可以很方便的管理所有者分组的同时,还能防止一些未知的恶意。 但是对于 Team 又有所不同,通过 `-add` 添加的 GitHub Team owner只拥有受限的权利。它们可以发布或 yank 某个版本,但是他们**不能添加或移除** owner总之Team 除了可以很方便的管理所有者分组的同时,还能防止一些未知的恶意。
如果大家在添加 team 时遇到问题,可以看看官方的[相关文档](https://doc.rust-lang.org/stable/cargo/reference/publishing.html#github-permissions),由于绝大多数人都无需此功能,因此这里不再详细展开。 如果大家在添加 team 时遇到问题,可以看看官方的[相关文档](https://doc.rust-lang.org/stable/cargo/reference/publishing.html#github-permissions),由于绝大多数人都无需此功能,因此这里不再详细展开。

@ -1,6 +1,6 @@
# 指定依赖项 # 指定依赖项
我们的项目可以引用在 `crates.io``github` 上的依赖包,也可以引用存放在本地文件系统中的依赖包。 我们的项目可以引用在 `crates.io``GitHub` 上的依赖包,也可以引用存放在本地文件系统中的依赖包。
大家可能会想,直接从前两个引用即可,为何还提供了本地方式?可以设想下,如果你要有一个正处于开发中的包,然后需要在本地的另一个项目中引用测试,那是将该包先传到网上,然后再引用简单,还是直接从本地路径的方式引用简单呢?答案显然不言而喻。 大家可能会想,直接从前两个引用即可,为何还提供了本地方式?可以设想下,如果你要有一个正处于开发中的包,然后需要在本地的另一个项目中引用测试,那是将该包先传到网上,然后再引用简单,还是直接从本地路径的方式引用简单呢?答案显然不言而喻。
@ -83,7 +83,7 @@ time = "0.1.12"
>= 1.2, < 1.5 >= 1.2, < 1.5
``` ```
需要注意,以上的版本号规则仅仅针对 `crate.io` 和基于它搭建的注册服务(例如科大服务源) ,其它注册服务(例如 github )有自己相应的规则。 需要注意,以上的版本号规则仅仅针对 `crate.io` 和基于它搭建的注册服务(例如科大服务源) ,其它注册服务(例如 GitHub )有自己相应的规则。
## 从其它注册服务引入依赖包 ## 从其它注册服务引入依赖包

@ -1 +0,0 @@
# 命令行

@ -1,3 +0,0 @@
# 场景化用例
https://rust-lang-nursery.github.io/rust-cookbook/

@ -0,0 +1,4 @@
# 实用算法
本章将收集一些在实战中经常使用的算法 API。
> Note: 这里没有具体的算法实现,都是关于如何应用的

@ -0,0 +1,155 @@
# 生成随机值
### 生成随机数
使用 [rand::thread_rng](https://docs.rs/rand/*/rand/fn.thread_rng.html) 可以获取一个随机数生成器 [rand::Rng](https://docs.rs/rand/0.8.5/rand/trait.Rng.html) ,该生成器需要在每个线程都初始化一个。
整数的随机分布范围等于类型的取值范围,但是浮点数只分布在 `[0, 1)` 区间内。
```rust,editable
use rand::Rng;
fn main() {
let mut rng = rand::thread_rng();
let n1: u8 = rng.gen();
let n2: u16 = rng.gen();
println!("Random u8: {}", n1);
println!("Random u16: {}", n2);
println!("Random u32: {}", rng.gen::<u32>());
println!("Random i32: {}", rng.gen::<i32>());
println!("Random float: {}", rng.gen::<f64>());
}
```
### 指定范围生成随机数
使用 [Rng::gen_range](https://rust-lang-nursery.github.io/rust-cookbook/algorithms/randomness.html) 生成 [0, 10) 区间内的随机数( 右开区间,不包括 `10` )。
```rust,editable
use rand::Rng;
fn main() {
let mut rng = rand::thread_rng();
println!("Integer: {}", rng.gen_range(0..10));
println!("Float: {}", rng.gen_range(0.0..10.0));
}
```
[Uniform](https://docs.rs/rand/*/rand/distributions/uniform/struct.Uniform.html) 可以用于生成<ruby>均匀分布<rt>uniform distribution</rt></ruby>的随机数。当需要在同一个范围内重复生成随机数时,该方法虽然和之前的方法效果一样,但会更快一些。
```rust,editable
use rand::distributions::{Distribution, Uniform};
fn main() {
let mut rng = rand::thread_rng();
let die = Uniform::from(1..7);
loop {
let throw = die.sample(&mut rng);
println!("Roll the die: {}", throw);
if throw == 6 {
break;
}
}
}
```
### 使用指定分布来生成随机数
默认情况下,`rand` 包使用均匀分布来生成随机数,而 [rand_distr](https://docs.rs/rand_distr/*/rand_distr/index.html) 包提供了其它类型的分布方式。
首先,你需要获取想要使用的分布的实例,然后在 [rand::Rng](https://docs.rs/rand/*/rand/trait.Rng.html) 的帮助下使用 [Distribution::sample](https://docs.rs/rand/*/rand/distributions/trait.Distribution.html#tymethod.sample) 对该实例进行取样。
如果想要查询可用的分布列表,可以访问[这里](https://docs.rs/rand_distr/*/rand_distr/index.html),下面的示例中我们将使用 [Normal](https://docs.rs/rand_distr/0.4.3/rand_distr/struct.Normal.html) 分布:
```rust,editable
use rand_distr::{Distribution, Normal, NormalError};
use rand::thread_rng;
fn main() -> Result<(), NormalError> {
let mut rng = thread_rng();
let normal = Normal::new(2.0, 3.0)?;
let v = normal.sample(&mut rng);
println!("{} is from a N(2, 9) distribution", v);
Ok(())
}
```
### 在自定义类型中生成随机值
使用 [Distribution](https://docs.rs/rand/*/rand/distributions/trait.Distribution.html) 特征包裹我们的自定义类型,并为 [Standard](https://docs.rs/rand/*/rand/distributions/struct.Standard.html) 实现该特征,可以为自定义类型的指定字段生成随机数。
```rust,editable
use rand::Rng;
use rand::distributions::{Distribution, Standard};
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
impl Distribution<Point> for Standard {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Point {
let (rand_x, rand_y) = rng.gen();
Point {
x: rand_x,
y: rand_y,
}
}
}
fn main() {
let mut rng = rand::thread_rng();
// 生成一个随机的 Point
let rand_point: Point = rng.gen();
println!("Random Point: {:?}", rand_point);
// 通过类型暗示( hint )生成一个随机的元组
let rand_tuple = rng.gen::<(i32, bool, f64)>();
println!("Random tuple: {:?}", rand_tuple);
}
```
### 生成随机的字符串(A-Z, a-z, 0-9)
通过 [Alphanumeric](https://docs.rs/rand/0.8.5/rand/distributions/struct.Alphanumeric.html) 采样来生成随机的 ASCII 字符串,包含从 `A-Z, a-z, 0-9` 的字符。
```rust,editble
use rand::{thread_rng, Rng};
use rand::distributions::Alphanumeric;
fn main() {
let rand_string: String = thread_rng()
.sample_iter(&Alphanumeric)
.take(30)
.map(char::from)
.collect();
println!("{}", rand_string);
}
```
### 生成随机的字符串( 用户指定 ASCII 字符 )
通过 [gen_string](https://docs.rs/rand/0.8.5/rand/trait.Rng.html#method.gen_range) 生成随机的 ASCII 字符串,包含用户指定的字符。
```rust,editable
fn main() {
use rand::Rng;
const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
abcdefghijklmnopqrstuvwxyz\
0123456789)(*&^%$#@!~";
const PASSWORD_LEN: usize = 30;
let mut rng = rand::thread_rng();
let password: String = (0..PASSWORD_LEN)
.map(|_| {
let idx = rng.gen_range(0..CHARSET.len());
CHARSET[idx] as char
})
.collect();
println!("{:?}", password);
}
```

@ -0,0 +1,84 @@
## Vector 排序
### 对整数 Vector 排序
以下示例使用 [Vec::sort](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.sort) 来排序,如果大家希望获得更高的性能,可以使用 [Vec::sort_unstable](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.sort_unstable),但是该方法无法保留相等元素的顺序。
```rust,editable
fn main() {
let mut vec = vec![1, 5, 10, 2, 15];
vec.sort();
assert_eq!(vec, vec![1, 2, 5, 10, 15]);
}
```
### 对浮点数 Vector 排序
浮点数数组可以使用 [Vec::sort_by](https://doc.rust-lang.org/std/primitive.slice.html#method.sort_by) 和 [PartialOrd::partial_cmp](https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html#tymethod.partial_cmp) 进行排序。
```rust,editable
fn main() {
let mut vec = vec![1.1, 1.15, 5.5, 1.123, 2.0];
vec.sort_by(|a, b| a.partial_cmp(b).unwrap());
assert_eq!(vec, vec![1.1, 1.123, 1.15, 2.0, 5.5]);
}
```
### 对结构体 Vector 排序
以下示例中的结构体 `Person` 将实现基于字段 `name``age` 的自然排序。为了让 `Person` 变为可排序的,我们需要为其派生 `Eq、PartialEq、Ord、PartialOrd` 特征,关于这几个特征的详情,请见[这里](https://course.rs/advance/confonding/eq.html)。
当然,还可以使用 [vec:sort_by](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.sort_by) 方法配合一个自定义比较函数,只按照 `age` 的维度对 `Person` 数组排序。
```rust,editable
#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)]
struct Person {
name: String,
age: u32
}
impl Person {
pub fn new(name: String, age: u32) -> Self {
Person {
name,
age
}
}
}
fn main() {
let mut people = vec![
Person::new("Zoe".to_string(), 25),
Person::new("Al".to_string(), 60),
Person::new("John".to_string(), 1),
];
// 通过派生后的自然顺序(Name and age)排序
people.sort();
assert_eq!(
people,
vec![
Person::new("Al".to_string(), 60),
Person::new("John".to_string(), 1),
Person::new("Zoe".to_string(), 25),
]);
// 只通过 age 排序
people.sort_by(|a, b| b.age.cmp(&a.age));
assert_eq!(
people,
vec![
Person::new("Al".to_string(), 60),
Person::new("Zoe".to_string(), 25),
Person::new("John".to_string(), 1),
]);
}
```

@ -0,0 +1,50 @@
# ANSI 终端
[ansi_term](https://crates.io/crates/ansi_term) 包可以帮我们控制终端上的输出样式,例如使用颜色文字、控制输出格式等,当然,前提是在 ANSI 终端上。
`ansi_term` 中有两个主要数据结构:[ANSIString](https://docs.rs/ansi_term/0.12.1/ansi_term/type.ANSIString.html) 和 [Style](https://docs.rs/ansi_term/0.12.1/ansi_term/struct.Style.html)。
`Style` 用于控制样式:颜色、加粗、闪烁等,而前者是一个带有样式的字符串。
## 颜色字体
```rust,editable
use ansi_term::Colour;
fn main() {
println!("This is {} in color, {} in color and {} in color",
Colour::Red.paint("red"),
Colour::Blue.paint("blue"),
Colour::Green.paint("green"));
}
```
## 加粗字体
比颜色复杂的样式构建需要使用 `Style` 结构体:
```rust,editable
use ansi_term::Style;
fn main() {
println!("{} and this is not",
Style::new().bold().paint("This is Bold"));
}
```
## 加粗和颜色
`Colour` 实现了很多跟 `Style` 类似的函数,因此可以实现链式调用。
```rust,editable
use ansi_term::Colour;
use ansi_term::Style;
fn main(){
println!("{}, {} and {}",
Colour::Yellow.paint("This is colored"),
Style::new().bold().paint("this is bold"),
// Colour 也可以使用 bold 方法进行加粗
Colour::Yellow.bold().paint("this is bold and colored"));
}
```

@ -0,0 +1,70 @@
# 参数解析
## Clap
下面的程序给出了使用 `clap` 来解析命令行参数的样式结构,如果大家想了解更多,在 `clap` [文档](https://docs.rs/clap/)中还给出了另外两种初始化一个应用的方式。
在下面的构建中,`value_of` 将获取通过 `with_name` 解析出的值。`short` 和 `long` 用于设置用户输入的长短命令格式,例如短命令 `-f` 和长命令 `--file`
```rust,editable
use clap::{Arg, App};
fn main() {
let matches = App::new("My Test Program")
.version("0.1.0")
.author("Hackerman Jones <hckrmnjones@hack.gov>")
.about("Teaches argument parsing")
.arg(Arg::with_name("file")
.short("f")
.long("file")
.takes_value(true)
.help("A cool file"))
.arg(Arg::with_name("num")
.short("n")
.long("number")
.takes_value(true)
.help("Five less than your favorite number"))
.get_matches();
let myfile = matches.value_of("file").unwrap_or("input.txt");
println!("The file passed is: {}", myfile);
let num_str = matches.value_of("num");
match num_str {
None => println!("No idea what your favorite number is."),
Some(s) => {
match s.parse::<i32>() {
Ok(n) => println!("Your favorite number must be {}.", n + 5),
Err(_) => println!("That's not a number! {}", s),
}
}
}
}
```
`clap` 针对上面提供的构建样式,会自动帮我们生成相应的使用方式说明。例如,上面代码生成的使用说明如下:
```shell
My Test Program 0.1.0
Hackerman Jones <hckrmnjones@hack.gov>
Teaches argument parsing
USAGE:
testing [OPTIONS]
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
OPTIONS:
-f, --file <file> A cool file
-n, --number <num> Five less than your favorite number
```
最后,再使用一些参数来运行下我们的代码:
```shell
$ cargo run -- -f myfile.txt -n 251
The file passed is: myfile.txt
Your favorite number must be 256.
```
## Structopt
@todo

@ -0,0 +1,3 @@
## 压缩
我们会对常用的压缩方法进行介绍,例如 `tar`, `gzip`, `lz4` 等。

@ -0,0 +1,77 @@
# 使用tar包
## 解压 tar 包
以下代码将解压缩( [GzDecoder](https://docs.rs/flate2/*/flate2/read/struct.GzDecoder.html) )当前目录中的 `archive.tar.gz` ,并将所有文件抽取出( [Archive::unpack](https://docs.rs/tar/*/tar/struct.Archive.html#method.unpack) )来后当入到当前目录中。
```rust,editable
use std::fs::File;
use flate2::read::GzDecoder;
use tar::Archive;
fn main() -> Result<(), std::io::Error> {
let path = "archive.tar.gz";
let tar_gz = File::open(path)?;
let tar = GzDecoder::new(tar_gz);
let mut archive = Archive::new(tar);
archive.unpack(".")?;
Ok(())
}
```
## 将目录压缩成 tar 包
以下代码将 `/var/log` 目录压缩成 `archive.tar.gz`:
- 创建一个 [File](https://doc.rust-lang.org/std/fs/struct.File.html) 文件,并使用 [GzEncoder](https://docs.rs/flate2/*/flate2/write/struct.GzEncoder.html) 和 [tar::Builder](https://docs.rs/tar/*/tar/struct.Builder.html) 对其进行包裹
- 通过 [Builder::append_dir_all](https://docs.rs/tar/*/tar/struct.Builder.html#method.append_dir_all) 将 `/var/log` 目录下的所有内容添加到压缩文件中,该文件在 `backup/logs` 目录下。
- [GzEncoder](https://docs.rs/flate2/*/flate2/write/struct.GzEncoder.html) 负责在写入压缩文件 `archive.tar.gz` 之前对数据进行压缩。
```rust,editable
use std::fs::File;
use flate2::Compression;
use flate2::write::GzEncoder;
fn main() -> Result<(), std::io::Error> {
let tar_gz = File::create("archive.tar.gz")?;
let enc = GzEncoder::new(tar_gz, Compression::default());
let mut tar = tar::Builder::new(enc);
tar.append_dir_all("backup/logs", "/var/log")?;
Ok(())
}
```
## 解压的同时删除指定的文件前缀
遍历目录中的文件 [Archive::entries](https://docs.rs/tar/*/tar/struct.Archive.html#method.entries),若解压前的文件名包含 `bundle/logs` 前缀,需要将前缀从文件名移除( [Path::strip_prefix](https://doc.rust-lang.org/std/path/struct.Path.html#method.strip_prefix) )后,再解压。
```rust,editable
use std::fs::File;
use std::path::PathBuf;
use flate2::read::GzDecoder;
use tar::Archive;
fn main() -> Result<()> {
let file = File::open("archive.tar.gz")?;
let mut archive = Archive::new(GzDecoder::new(file));
let prefix = "bundle/logs";
println!("Extracted the following files:");
archive
.entries()? // 获取压缩档案中的文件条目列表
.filter_map(|e| e.ok())
// 对每个文件条目进行 map 处理
.map(|mut entry| -> Result<PathBuf> {
// 将文件路径名中的前缀移除,获取一个新的路径名
let path = entry.path()?.strip_prefix(prefix)?.to_owned();
// 将内容解压到新的路径名中
entry.unpack(&path)?;
Ok(path)
})
.filter_map(|e| e.ok())
.for_each(|x| println!("> {}", x.display()));
Ok(())
}
```

@ -0,0 +1,22 @@
# 场景化用例
对于开发者而言CookBook 是非常实用的,几乎每一门编程语言都是如此。原因无他:聪明的开发者大部分时间不是在复制粘贴就是在复制粘贴的路上。而 CookBook 恰恰为各种实用场景提供了可供直接复制粘贴的代码,例如文件操作、随机数生成、命令行解析等等,
> CookBook 的部分内容翻译自 [Rust CookBook](https://rust-lang-nursery.github.io/rust-cookbook/intro.html),但是内容并不相同,因为我们对部分内容进行了整合,最重要的是增加了大量实用库和代码片段
## 这本书的读者
本书适合所有程度的 Rust 开发者使用:
- 新手用来熟悉生态和常用库
- 老手在写代码时,可以直接用来复制粘贴,大幅提升工作效率
毕竟咱不是在面试造飞机,谁脑袋中能记住文件操作的各种细节,对不?
## 怎么使用
Cookbook 中的代码都是完整的,换而言之,这些代码片段包含了 `fn main` 函数,可以直接运行,如果你是拷贝到自己的代码中,请注意拷贝相应的代码部分,而不是全盘复制。
同时,这些代码( 大部分 )支持在线编辑和运行,大家无需复制到 IDE 中即可进行把玩研究。

@ -1 +1 @@
# Github # GitHub

@ -44,10 +44,14 @@
## A ## A
| 名称 | 关键字 | 简介 | | 名称 | 关键字 | 简介 |
| ---- | ------ | ------------- | | ------------- | -------- | ---------------------------------------------------------------------------------------------------------------------- |
| [] | | | | [array 数组] | 数组 | 长度固定<br>元素必须有相同的类型<br>依次线性排列<br>可以通过索引访问其中的元素<br>`let a: [i32; 5] = [1, 2, 3, 4, 5];` |
| A | KWA | AIntroduction | | [array slice] | 数组切片 | `let slice: &[i32] = &a[1..3];` |
| A | KWA | AIntroduction |
[array 数组]: https://course.rs/basic/compound-type/array.html
[array slice]: https://course.rs/basic/compound-type/array.html#数组切片
[back](#head) [back](#head)

@ -130,7 +130,7 @@ Rust 语言表达能力更强,性能更高。同时线程安全方面 Rust 也
- Google 除了在安卓系统的部分模块中使用 Rust 外,还在它最新的操作系统 Fuchsia 中重度使用 Rust - Google 除了在安卓系统的部分模块中使用 Rust 外,还在它最新的操作系统 Fuchsia 中重度使用 Rust
- Facebook 使用 Rust 来增强自己的网页端、移动端和 API 服务的性能,同时还写了 Hack 编程语言的虚拟机 - Facebook 使用 Rust 来增强自己的网页端、移动端和 API 服务的性能,同时还写了 Hack 编程语言的虚拟机
- Microsoft 使用 Rust 为 Azure 平台提供一些组件,其中包括 IoT 的核心服务 - Microsoft 使用 Rust 为 Azure 平台提供一些组件,其中包括 IoT 的核心服务
- Github 和 npmjs.com使用 Rust 提供高达每天 13 亿次的 npm 包下载 - GitHub 和 npmjs.com使用 Rust 提供高达每天 13 亿次的 npm 包下载
- Rust 目前已经成为全世界区块链平台的首选开发语言 - Rust 目前已经成为全世界区块链平台的首选开发语言
- TiDB国内最有名的开源分布式数据库 - TiDB国内最有名的开源分布式数据库

@ -0,0 +1,3 @@
## 课后习题
[https://practice.rs](https://practice.rs)

@ -1,6 +1,6 @@
# 用 Github Actions 进行持续集成 # 用 GitHub Actions 进行持续集成
[Github Actions](https://github.com/features/actions) 是官方于 2018 年推出的持续集成服务,它非常强大,本文将手把手带领大家学习如何使用 `Github Actions` 对 Rust 项目进行持续集成。 [GitHub Actions](https://github.com/features/actions) 是官方于 2018 年推出的持续集成服务,它非常强大,本文将手把手带领大家学习如何使用 `GitHub Actions` 对 Rust 项目进行持续集成。
持续集成是软件开发中异常重要的一环,大家应该都听说过 `Jenkins`,它就是一个拥有悠久历史的持续集成工具。简单来说,持续集成会定期拉取同一个项目中所有成员的相关代码,对其进行自动化构建。 持续集成是软件开发中异常重要的一环,大家应该都听说过 `Jenkins`,它就是一个拥有悠久历史的持续集成工具。简单来说,持续集成会定期拉取同一个项目中所有成员的相关代码,对其进行自动化构建。
@ -8,11 +8,11 @@
在有了持续集成后,只要编写好相应的编译、测试、发布配置文件,那持续集成平台会自动帮助我们完成整个相关的流程,期间无需任何人介入,高效且可靠。 在有了持续集成后,只要编写好相应的编译、测试、发布配置文件,那持续集成平台会自动帮助我们完成整个相关的流程,期间无需任何人介入,高效且可靠。
## Github Actions ## GitHub Actions
而本文的主角正是这样的持续集成平台,它由 Github 官方提供,并且跟 github 进行了深度的整合,其中 `actions` 代表了代码拉取、测试运行、登陆远程服务器、发布到第三方服务等操作行为。 而本文的主角正是这样的持续集成平台,它由 GitHub 官方提供,并且跟 GitHub 进行了深度的整合,其中 `actions` 代表了代码拉取、测试运行、登陆远程服务器、发布到第三方服务等操作行为。
最妙的是 Github 发现这些 `actions` 其实在很多项目中都是类似的,意味着 `actions` 完全可以被多个项目共享使用,而不是每个项目都从零开发自己的 `actions` 最妙的是 GitHub 发现这些 `actions` 其实在很多项目中都是类似的,意味着 `actions` 完全可以被多个项目共享使用,而不是每个项目都从零开发自己的 `actions`
若你需要某个 `action`,不必自己写复杂的脚本,直接引用他人写好的 `action` 即可,整个持续集成过程,就变成了多个 `action` 的组合,这就是` GitHub Actions` 最厉害的地方。 若你需要某个 `action`,不必自己写复杂的脚本,直接引用他人写好的 `action` 即可,整个持续集成过程,就变成了多个 `action` 的组合,这就是` GitHub Actions` 最厉害的地方。
@ -20,8 +20,8 @@
既然 `action` 这么强大,我们就可以将自己的 `action` 分享给他人,也可以引用他人分享的 `action`,有以下几种方式: 既然 `action` 这么强大,我们就可以将自己的 `action` 分享给他人,也可以引用他人分享的 `action`,有以下几种方式:
1. 将你的 `action` 放在 github 上的公共仓库中,这样其它开发者就可以引用,例如 [github-profile-summary-cards](https://github.com/vn7n24fzkq/github-profile-summary-cards) 就提供了相应的 `action`,可以生成 github 用户统计信息,然后嵌入到你的个人主页中,具体效果[见这里](https://github.com/sunface) 1. 将你的 `action` 放在 GitHub 上的公共仓库中,这样其它开发者就可以引用,例如 [github-profile-summary-cards](https://github.com/vn7n24fzkq/github-profile-summary-cards) 就提供了相应的 `action`,可以生成 GitHub 用户统计信息,然后嵌入到你的个人主页中,具体效果[见这里](https://github.com/sunface)
2. Github 提供了一个[官方市场](https://github.com/marketplace?type=actions),里面收集了许多质量不错的 `actions`,并支持在线搜索 2. GitHub 提供了一个[官方市场](https://github.com/marketplace?type=actions),里面收集了许多质量不错的 `actions`,并支持在线搜索
3. [awesome-actions](https://github.com/sdras/awesome-actions),由三方开发者收集并整理的 actions 3. [awesome-actions](https://github.com/sdras/awesome-actions),由三方开发者收集并整理的 actions
4. [starter workflows](https://github.com/actions/starter-workflows),由官方提供的工作流( workflow )模版 4. [starter workflows](https://github.com/actions/starter-workflows),由官方提供的工作流( workflow )模版
@ -39,18 +39,18 @@ actions/setup-node@f099707 # 指向一个 commit
## Actions 基础 ## Actions 基础
在了解了何为 Github Actions 后,再来通过一个基本的例子来学习下它的基本概念,注意,由于篇幅有限,我们只会讲解最常用的部分,如果想要完整的学习,请移步[这里](https://docs.github.com/en/actions)。 在了解了何为 GitHub Actions 后,再来通过一个基本的例子来学习下它的基本概念,注意,由于篇幅有限,我们只会讲解最常用的部分,如果想要完整的学习,请移步[这里](https://docs.github.com/en/actions)。
#### 创建 action demo #### 创建 action demo
首先,为了演示,我们需要创建一个公开的 github 仓库 `rust-action`,然后在仓库主页的导航栏中点击 `Actions` ,你会看到如下页面 : 首先,为了演示,我们需要创建一个公开的 GitHub 仓库 `rust-action`,然后在仓库主页的导航栏中点击 `Actions` ,你会看到如下页面 :
<img src="https://pic1.zhimg.com/80/v2-4bb58f042c7a285219910bfd3c259464_1440w.jpg" /> <img src="https://pic1.zhimg.com/80/v2-4bb58f042c7a285219910bfd3c259464_1440w.jpg" />
接着点击 `set up a workflow yourself ->` ,你将看到系统为你自动创建的一个工作流 workflow ,在 `rust-action/.github/workflows/main.yml` 文件中包含以下内容: 接着点击 `set up a workflow yourself ->` ,你将看到系统为你自动创建的一个工作流 workflow ,在 `rust-action/.github/workflows/main.yml` 文件中包含以下内容:
```yml ```yml
# 下面是一个基础的工作流,你可以基于它来编写自己的 Github Actions # 下面是一个基础的工作流,你可以基于它来编写自己的 GitHub Actions
name: CI name: CI
# 控制工作流何时运行 # 控制工作流何时运行
@ -113,23 +113,23 @@ jobs:
<img src="https://pic1.zhimg.com/80/v2-94b46f23b5d63de35eae7f0425bb99b7_1440w.jpg" /> <img src="https://pic1.zhimg.com/80/v2-94b46f23b5d63de35eae7f0425bb99b7_1440w.jpg" />
至此,我们已经初步掌握 `Github Actions` 的用法,现在来看看一些基本的概念。 至此,我们已经初步掌握 `GitHub Actions` 的用法,现在来看看一些基本的概念。
#### 基本概念 #### 基本概念
- **Github Actions**,每个项目都拥有一个 `Actions` ,可以包含多个工作流 - **GitHub Actions**,每个项目都拥有一个 `Actions` ,可以包含多个工作流
- **workflow 工作流**,描述了一次持续集成的过程 - **workflow 工作流**,描述了一次持续集成的过程
- **job 作业**,一个工作流可以包含多个作业,因为一次持续集成本身就由多个不同的部分组成 - **job 作业**,一个工作流可以包含多个作业,因为一次持续集成本身就由多个不同的部分组成
- **step 步骤**,每个作业由多个步骤组成,按照顺序一步一步完成 - **step 步骤**,每个作业由多个步骤组成,按照顺序一步一步完成
- **action 动作**,每个步骤可以包含多个动作,例如上例中的 `Run a multi-line script` 步骤就包含了两个动作 - **action 动作**,每个步骤可以包含多个动作,例如上例中的 `Run a multi-line script` 步骤就包含了两个动作
可以看出,每一个概念都是相互包含的关系,前者包含了后者,层层相扣,正因为这些精心设计的对象才有了强大的 `Github Actions`。 可以看出,每一个概念都是相互包含的关系,前者包含了后者,层层相扣,正因为这些精心设计的对象才有了强大的 `GitHub Actions`。
#### on #### on
`on` 可以设定事件用于触发工作流的运行: `on` 可以设定事件用于触发工作流的运行:
1. 一个或多个 Github 事件,例如 `push` 一个 `commit`、创建一个 `issue`、提交一次 `pr` 等等,详细的事件列表参见[这里](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows) 1. 一个或多个 GitHub 事件,例如 `push` 一个 `commit`、创建一个 `issue`、提交一次 `pr` 等等,详细的事件列表参见[这里](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows)
2. 预定的时间,例如每天零点零分触发,详情见[这里](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule) 2. 预定的时间,例如每天零点零分触发,详情见[这里](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule)
@ -138,7 +138,7 @@ on:
schedule: -cron:'0 0 * * *' schedule: -cron:'0 0 * * *'
``` ```
3. 外部事件触发,例如你可以通过 `REST API` 向 Github 发送请求去触发,具体请查阅[官方文档](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#repository_dispatch) 3. 外部事件触发,例如你可以通过 `REST API` 向 GitHub 发送请求去触发,具体请查阅[官方文档](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#repository_dispatch)
#### jobs #### jobs
@ -159,9 +159,9 @@ jobs:
#### runs-on #### runs-on
指定作业的运行环境,运行器 `runner` 分为两种:`GitHub-hosted runner` 和 `self-hosted runner`,后者是使用自己的机器来运行作业,但是需要 Github 能进行访问并给予相应的机器权限,感兴趣的同学可以看看[这里](https://docs.github.com/en/actions/using-jobs/choosing-the-runner-for-a-job#choosing-self-hosted-runners)。 指定作业的运行环境,运行器 `runner` 分为两种:`GitHub-hosted runner` 和 `self-hosted runner`,后者是使用自己的机器来运行作业,但是需要 GitHub 能进行访问并给予相应的机器权限,感兴趣的同学可以看看[这里](https://docs.github.com/en/actions/using-jobs/choosing-the-runner-for-a-job#choosing-self-hosted-runners)。
而对于前者Github 提供了以下的运行环境: 而对于前者GitHub 提供了以下的运行环境:
<img src="https://pic2.zhimg.com/80/v2-614999565cc513715aaf156c2e478991_1440w.jpg" /> <img src="https://pic2.zhimg.com/80/v2-614999565cc513715aaf156c2e478991_1440w.jpg" />
@ -221,18 +221,18 @@ jobs:
如果有多个 `env` 存在,会使用就近那个。 如果有多个 `env` 存在,会使用就近那个。
至此,`Github Actions` 的常用内容大家已经基本了解,下面来看一个实用的示例。 至此,`GitHub Actions` 的常用内容大家已经基本了解,下面来看一个实用的示例。
## 真实示例:生成 Github 统计卡片 ## 真实示例:生成 GitHub 统计卡片
相信大家看过不少用户都定制了自己的个性化 Github 首页,这个是通过在个人名下创建一个同名的仓库来实现的,该仓库中的 `Readme.md` 的内容会自动展示在你的个人首页中,例如 `Sunface` 的[个人首页](https://github.com/sunface) 和内容所在的[仓库](https://github.com/sunface/sunface)。 相信大家看过不少用户都定制了自己的个性化 GitHub 首页,这个是通过在个人名下创建一个同名的仓库来实现的,该仓库中的 `Readme.md` 的内容会自动展示在你的个人首页中,例如 `Sunface` 的[个人首页](https://github.com/sunface) 和内容所在的[仓库](https://github.com/sunface/sunface)。
大家可能会好奇上面链接中的 Github 统计卡片如何生成,其实有两种办法: 大家可能会好奇上面链接中的 GitHub 统计卡片如何生成,其实有两种办法:
- 使用 [github-readme-stats](https://github.com/anuraghazra/github-readme-stats) - 使用 [github-readme-stats](https://github.com/anuraghazra/github-readme-stats)
- 使用 `Github Actions` 来引用其它人提供的 `action` 生成对应的卡片,再嵌入进来, `Sunface` 的个人首页就是这么做的 - 使用 `GitHub Actions` 来引用其它人提供的 `action` 生成对应的卡片,再嵌入进来, `Sunface` 的个人首页就是这么做的
第一种的优点就是非常简单,缺点是样式不太容易统一,不能对齐对于强迫症来说实在难受 :( 而后者的优点是规规整整的卡片,缺点就是使用起来更加复杂,而我们正好借此来看看真实的 `Github Actions` 长什么样。 第一种的优点就是非常简单,缺点是样式不太容易统一,不能对齐对于强迫症来说实在难受 :( 而后者的优点是规规整整的卡片,缺点就是使用起来更加复杂,而我们正好借此来看看真实的 `GitHub Actions` 长什么样。
首先,在你的同名项目下创建 `.github/workflows/profile-summary-cards.yml` 文件,然后填入以下内容: 首先,在你的同名项目下创建 `.github/workflows/profile-summary-cards.yml` 文件,然后填入以下内容:

@ -198,4 +198,4 @@ Rust 提供了单元测试和集成测试两种方式来帮助我们组织测试
- 单元测试的模块和待测试的代码在同一个文件中,且可以很方便地对私有函数进行测试 - 单元测试的模块和待测试的代码在同一个文件中,且可以很方便地对私有函数进行测试
- 集成测试文件放在项目根目录下的 `tests` 目录中,由于该目录下每个文件都是一个包,我们必须要引入待测试的代码到当前包的作用域中,才能进行测试,正因为此,集成测试只能对声明为 `pub` 的 API 进行测试 - 集成测试文件放在项目根目录下的 `tests` 目录中,由于该目录下每个文件都是一个包,我们必须要引入待测试的代码到当前包的作用域中,才能进行测试,正因为此,集成测试只能对声明为 `pub` 的 API 进行测试
下个章节,我们再来看看该如何使用 `Github Actions` 对 Rust 项目进行持续集成。 下个章节,我们再来看看该如何使用 `GitHub Actions` 对 Rust 项目进行持续集成。

@ -54,7 +54,7 @@
对于前者,那性能如何自然无关紧要。而对于后者,我们只需要使用 `Vec::with_capacity` 提前分配足够的空间即可同时Rust 中所有的迭代器还提供了 `size_hint` 也可以解决这种问题。 对于前者,那性能如何自然无关紧要。而对于后者,我们只需要使用 `Vec::with_capacity` 提前分配足够的空间即可同时Rust 中所有的迭代器还提供了 `size_hint` 也可以解决这种问题。
当然,如果这段代码在热点路径,且你无法提前预测所需的容量,那么链表确实会更节省性能。 当然,如果这段代码在热点路径,且你无法提前预测所需的容量,那么链表确实会更提升性能。
#### 链表更节省内存空间 #### 链表更节省内存空间
首先,这个问题较为复杂。一个标准的数组调整策略是:增加或减少数组的长度使数组最多有一半为空,例如 capacity 增长是翻倍的策略。这确实会导致内存空间的浪费,特别是在 Rust 中,我们不会自动收缩集合类型。 首先,这个问题较为复杂。一个标准的数组调整策略是:增加或减少数组的长度使数组最多有一半为空,例如 capacity 增长是翻倍的策略。这确实会导致内存空间的浪费,特别是在 Rust 中,我们不会自动收缩集合类型。

@ -92,4 +92,9 @@ table {
.fa-github:after{ .fa-github:after{
content: "繁星点点尽在你的指尖 🌟"; content: "繁星点点尽在你的指尖 🌟";
margin-left: 4px; margin-left: 4px;
}
/* Fix on mobile device */
code {
word-break: break-word;
} }

@ -1,6 +1,15 @@
# ChangeLog # ChangeLog
记录一些值得注意的变更。 记录一些值得注意的变更。
## 2022-04-01
- 新增章节: [Cookbook - 生成随机值](https://course.rs/cookbook/algos/randomness.html)
- 新增章节: [Cookbook - Vec排序](https://course.rs/cookbook/algos/sorting.html)
- 新增章节: [Cookbook - 命令行参数解析](https://course.rs/cookbook/cmd/parsing.html)
- 新增章节:[Cookbook - 终端输出格式化](https://course.rs/cookbook/cmd/ansi.html)
- 新增章节:[Cookbook - 压缩 tar 包](https://course.rs/cookbook/compression/tar.html)
## 2022-03-29 ## 2022-03-29
- 新增章节: [栈上的链表](https://course.rs/too-many-lists/advanced-lists/stack-allocated.html) - 新增章节: [栈上的链表](https://course.rs/too-many-lists/advanced-lists/stack-allocated.html)
@ -9,7 +18,7 @@
## 2022-03-28 ## 2022-03-28
- 新增章节:[双单向链表](https://course.rs/too-many-lists/advanced-lists/double-singly.html) - 新增章节:[双单向链表](https://course.rs/too-many-lists/advanced-lists/double-singly.html)
- 优化样式:增加目录中的区域性标题、修改 github 图标和说明,通过 js 增加访问者统计 - 优化样式:增加目录中的区域性标题、修改 GitHub 图标和说明,通过 js 增加访问者统计
- 新增创作感悟 - 新增创作感悟
## 2022-03-27 ## 2022-03-27

Loading…
Cancel
Save