From abf76ebdaefb205b00e1a9d59a919e9a2114a0b5 Mon Sep 17 00:00:00 2001 From: sunface Date: Tue, 30 Nov 2021 14:30:49 +0800 Subject: [PATCH] add appendix --- src/SUMMARY.md | 9 +- src/appendix/a-keywords | 1 + src/appendix/a-keywords.md | 105 +++++++++++++++++ src/appendix/intro.md | 2 + src/basic/variable.md | 131 ++++++++++++++++++++- src/intro.md | 18 +++ src/style-guide/intro.md | 231 +------------------------------------ src/why-another.md | 12 +- 8 files changed, 265 insertions(+), 244 deletions(-) create mode 100644 src/appendix/a-keywords create mode 100644 src/appendix/a-keywords.md create mode 100644 src/appendix/intro.md create mode 100644 src/intro.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index ec2cf329..13f7b2e3 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -14,7 +14,7 @@ ## Rust学习三部曲 - [基本概念](basic/intro.md) - - [变量绑定](basic/variable.md) + - [变量与绑定](basic/variable.md) - [基本类型](basic/type.md) - [复合类型](basic/compound-type.md) - [函数与方法](basic/function-method.md) @@ -68,4 +68,9 @@ ## 场景模版 -- [场景示例](use-case/intro.md) \ No newline at end of file +- [场景示例](use-case/intro.md) + + +## 附录 +- [附录](appendix/intro.md) + - [A-关键字](appendix/a-keywords) \ No newline at end of file diff --git a/src/appendix/a-keywords b/src/appendix/a-keywords new file mode 100644 index 00000000..7eacb74f --- /dev/null +++ b/src/appendix/a-keywords @@ -0,0 +1 @@ +# A-关键字 diff --git a/src/appendix/a-keywords.md b/src/appendix/a-keywords.md new file mode 100644 index 00000000..0270cff0 --- /dev/null +++ b/src/appendix/a-keywords.md @@ -0,0 +1,105 @@ +## 附录 A:关键字 + +下面的列表包含 Rust 中正在使用或者以后会用到的关键字。因此,这些关键字不能被用作标识符(除了 “[原生标识符](#原生标识符)” 部分介绍的原始标识符),包括函数、变量、参数、结构体字段、模块、包、常量、宏、静态值、属性、类型、特征或生命周期。 + + +### 目前正在使用的关键字 + +如下关键字目前有对应其描述的功能。 + +* `as` - 强制类型转换,或`use` 和 `extern crate`包和模块引入语句中的重命名 +* `break` - 立刻退出循环 +* `const` - 定义常量或原生常量指针(constant raw pointer) +* `continue` - 继续进入下一次循环迭代 +* `crate` - 链接外部包 +* `dyn` - 动态分发特征对象 +* `else` - 作为 `if` 和 `if let` 控制流结构的 fallback +* `enum` - 定义一个枚举类型 +* `extern` - 链接一个外部包,或者一个宏变量(该变量定义在另外一个包中) +* `false` - 布尔值 `false` +* `fn` - 定义一个函数或 **函数指针类型** (*function pointer type*) +* `for` - 遍历一个迭代器或实现一个 trait 或者指定一个更高级的生命周期 +* `if` - 基于条件表达式的结果来执行相应的分支 +* `impl` - 为结构体或者特征实现具体功能 +* `in` - `for` 循环语法的一部分 +* `let` - 绑定一个变量 +* `loop` - 无条件循环 +* `match` - 模式匹配 +* `mod` - 定义一个模块 +* `move` - 使闭包获取其所捕获项的所有权 +* `mut` - 在引用、原生指针或模式绑定中使用,表明变量是可变的 +* `pub` - 表示结构体字段、`impl` 块或模块的公共可见性 +* `ref` - 通过引用绑定 +* `return` - 从函数中返回 +* `Self` - 实现特征类型的类型别名 +* `self` - 表示方法本身或当前模块 +* `static` - 表示全局变量或在整个程序执行期间保持其生命周期 +* `struct` - 定义一个结构体 +* `super` - 表示当前模块的父模块 +* `trait` - 定义一个特征 +* `true` - 布尔值 `true` +* `type` - 定义一个类型别名或关联类型 +* `unsafe` - 表示不安全的代码、函数、特征或实现 +* `use` - 在当前代码范围内(模块或者花括号对)引入外部的包、模块等 +* `where` - 表示一个约束类型的从句 +* `while` - 基于一个表达式的结果判断是否继续循环 + +### 保留做将来使用的关键字 + +如下关键字没有任何功能,不过由 Rust 保留以备将来的应用。 + +* `abstract` +* `async` +* `await` +* `become` +* `box` +* `do` +* `final` +* `macro` +* `override` +* `priv` +* `try` +* `typeof` +* `unsized` +* `virtual` +* `yield` + +### 原生标识符 + +原生标识符(Raw identifiers)允许你使用通常不能使用的关键字,其带有 `r#` 前缀。 + +例如,`match` 是关键字。如果尝试编译如下使用 `match` 作为名字的函数: + +```rust,ignore,does_not_compile +fn match(needle: &str, haystack: &str) -> bool { + haystack.contains(needle) +} +``` + +会得到这个错误: + +```text +error: expected identifier, found keyword `match` + --> src/main.rs:4:4 + | +4 | fn match(needle: &str, haystack: &str) -> bool { + | ^^^^^ expected identifier, found keyword +``` + +该错误表示你不能将关键字 `match` 用作函数标识符。你可以使用原生标识符将 `match` 作为函数名称使用: + +文件名: src/main.rs + +```rust +fn r#match(needle: &str, haystack: &str) -> bool { + haystack.contains(needle) +} + +fn main() { + assert!(r#match("foo", "foobar")); +} +``` + +此代码编译没有任何错误。注意 `r#` 前缀需同时用于函数名定义和 `main` 函数中的调用。 + +原生标识符允许使用你选择的任何单词作为标识符,即使该单词恰好是保留关键字。 此外,原生标识符允许你使用其它Rust版本编写的库。比如,`try` 在Rust 2015 edition 中不是关键字,却在Rust 2018 edition是关键字。所以如果用2015 edition 编写的库中带有 `try` 函数,在 2018 edition 中调用时就需要使用原始标识符语法,在这里是 `r#try`。 diff --git a/src/appendix/intro.md b/src/appendix/intro.md new file mode 100644 index 00000000..56901574 --- /dev/null +++ b/src/appendix/intro.md @@ -0,0 +1,2 @@ +# 附录 +附录中包含了一些Rust常用概念的快速索引,方便你的日常查询使用 \ No newline at end of file diff --git a/src/basic/variable.md b/src/basic/variable.md index 02045346..93cf45c3 100644 --- a/src/basic/variable.md +++ b/src/basic/variable.md @@ -1 +1,130 @@ -# 变量与类型 +# 变量与绑定 + +鉴于本书的[目标读者](../intro.md)已经熟练掌握其它任意一门编程语言,因此这里就不再对何为变量进行赘述,让我们开门见山来谈谈,为何Rust选择了手动设定变量可变性。 + +## 为何要手动设置变量的可变性? + +在其它大多数语言中,变量一旦创建,要么是可变的,要么是不可变的(ClosureScript),前者为编程提供了灵活性,后者为编程提供了安全性,而Rust选择了两者我都要,既要灵活性又要安全性。 + +能想要学习Rust,说明我们的读者都是相当有水平的程序员了,你们应该能理解一切选择都是权衡,那么两者都要的权衡是什么呢?这就是Rust开发团队为我们做出的贡献,两者都要意味着底层代码的实现复杂度大幅提升,Respect to The Rust Team! + +除了以上两个优点,还有一个很大的优点,那就是运行性能上的提升,因为不可变的变量在运行期会避免一些多余的runtime检查。 + +## 变量命名 + +在命名方面,和其它语言没有区别,不过当你给变量命名时,需要遵循[Rust命名规范](../style-guide/naming.md)。 + +> ### 关键字 +> +> Rust语言有一些**关键字**(*keywords*),和其他语言一样,这些关键字都是被保留给Rust语言使用的,因此,这些关键字不能被用作变量或函数的名称。 在[附录 A](../appendix/a-keywords) 中可找到关键字列表。 + +Keywords + +The Rust language has a set of keywords that are reserved for use by the language only, much as in other languages. Keep in mind that you cannot use these words as names of variables or functions. Most of the keywords have special meanings, and you’ll be using them to do various tasks in your Rust programs; a few have no current functionality associated with them but have been reserved for functionality that might be added to Rust in the future. You can find a list of the keywords in Appendix A. + +,默认情况下变量是**不可变的**(*immutable*)。这是 Rust 中众多精妙之处的其中一个,Rust 的这些设计点鼓励你以一种充分利用 Rust 提供的安全和简单并发的方式来编写代码。不过你也可以选择让变量是**可变的**(*mutable*)。让我们来探讨为什么 Rust 鼓励你选用不可变性,以及为什么你可能不喜欢这样。 + +当变量不可变时,这意味着一旦一个值绑定到一个变量名后,就不能再更改该值了。为了说明,我们在 *projects* 目录下使用 `cargo new variables` 来创建一个名为 *variables* 新项目。 + +然后在新建的 *variables* 目录下,打开 *src/main.rs* 并将代码替换为下面还未能通过编译的代码: + +文件名:src/main.rs + +```rust,ignore,does_not_compile +{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-01-variables-are-immutable/src/main.rs}} +``` + +保存文件,并使用 `cargo run` 运行程序。你将会收到一条错误消息,输出如下所示: + +```console +{{#include ../listings/ch03-common-programming-concepts/no-listing-01-variables-are-immutable/output.txt}} +``` + +这个例子展示了编译器如何帮助你查找程序中的错误。即使编译器错误可能令人沮丧,它们也只是表明你的程序做你想做的事情并不安全;并**不**意味着你不是一个好开发者!有经验的 Rustaceans 依然会遇到编译错误。 + +上面的错误指出错误的原因是 `cannot +assign twice to immutable variable x`(不能对不可变变量二次赋值),因为我们尝试给不可变的 `x` 变量赋值为第二个值。 + +当我们尝试改变一个前面指定为不可变的值时我们会得到编译期错误,这点很重要,因为这种情况很可能导致 bug。如果我们代码的一部分假设某个值永远不会更改,而代码的另一部分更改了该值,那很可能第一部分代码不会按照所设计的逻辑运行。这个 bug 的根源在实际开发中可能很难追踪,特别是第二部分代码只是**偶尔**变更了原来的值。 + +在 Rust 中,编译器保证了当我们声明了一个值不会改变,那它就真的不可改变。这意味着当你正在阅读和编写代码时,就不必跟踪一个值怎样变化以及在哪发生改变,这可以使得代码更容易理解。 + +但可变性有时也相当重要。变量只是默认不可变的;我们可以通过在变量名前加上 `mut` 使得它们可变。除了允许这个值改变外,它还向以后的读代码的人传达了这样的意思:代码的其他部分将会改变这个变量值。 + +例如将 *src/main.rs* 改为以下内容: + +文件名:src/main.rs + + +```rust +{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-02-adding-mut/src/main.rs}} +``` + +运行程序将得到下面结果: + +```console +{{#include ../listings/ch03-common-programming-concepts/no-listing-02-adding-mut/output.txt}} +``` + +加上 `mut` 后,我们就可以将 `x` 绑定的值从 `5` 改成 `6`。在一些情况下,你需要变量是可变的,因为相比只使用不可变变量的实现,这可使得代码更容易编写。 + +除了预防 bug 外,还有很多权衡要考虑。例如,在使用大型数据结构的情形下,在同一位置更改实例可能比复制并返回新分配的实例要更快。使用较小的数据结构时,通常创建新的实例并以更具函数式的风格来编写程序,可能会更容易理解,所以值得以较低的性能开销来确保代码清晰。 + +### 变量和常量之间的差异 + +变量的值不能更改可能让你想起其他另一个很多语言都有的编程概念:**常量**(*constant*)。与不可变变量一样,常量也是绑定到一个常量名且不允许更改的值,但是常量和变量之间存在一些差异。 + +首先,常量不允许使用 `mut`。常量不仅仅默认不可变,而且自始至终不可变。 + +常量使用 `const` 关键字而不是 `let` 关键字来声明,并且值的类型**必须**标注。我们将在下一节[“数据类型”][data-types]中介绍类型和类型标注,因此现在暂时不需关心细节。只需知道你必须始终对类型进行标注。 + +常量可以在任意作用域内声明,包括全局作用域,这对于代码中很多部分都需要知道一个值的情况特别有用。 + +最后一个不同点是常量只能设置为常量表达式,而不能是函数调用的结果或是只能在运行时计算得到的值。 + +下面是一个常量声明的例子,其常量名为 `MAX_POINTS`,值设置为 100,000。(Rust 常量的命名约定是全部字母都使用大写,并使用下划线分隔单词,另外对数字字面量可插入下划线以提高可读性): + +```rust +const MAX_POINTS: u32 = 100_000; +``` + +在声明的作用域内,常量在程序运行的整个过程中都有效。对于应用程序域中程序的多个部分可能都需要知道的值的时候,常量是一个很有用的选择,例如游戏中允许玩家赚取的最大点数或光速。 + +将整个程序中用到的硬编码(hardcode)值命名为常量,对于将该值的含义传达给代码的未来维护者很有用。如果将来需要更改硬编码的值,则只需要在代码中改动一处就可以了。 + +### 变量遮蔽 + +正如你在第 2 章[“猜数字游戏”][comparing-the-guess-to-the-secret-number]章节中所看到的,你可以声明和前面变量具有相同名称的新变量。Rustaceans 说这个是第一个变量被第二个变量**遮蔽**(*shadow*),这意味着当我们使用变量时我们看到的会是第二个变量的值。我们可以通过使用相同的变量名并重复使用 `let` 关键字来遮蔽变量,如下所示: + +文件名:src/main.rs + +```rust +{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-03-shadowing/src/main.rs}} +``` + +这个程序首先将数值 `5` 绑定到 `x`。然后通过重复使用 `let x =` 来遮蔽之前的 `x`,并取原来的值加上 `1`,所以 `x` 的值变成了 `6`。第三个 `let` 语句同样遮蔽前面的 `x`,取之前的值并乘上 `2`,得到的 `x` 最终值为 `12`。当运行此程序,将输出以下内容: + +```console +{{#include ../listings/ch03-common-programming-concepts/no-listing-03-shadowing/output.txt}} +``` + +这和将变量标记为 `mut` 的方式不同,因为除非我们再次使用 `let` 关键字,否则若是我们不小心尝试重新赋值给这个变量,我们将得到一个编译错误。通过使用 `let`,我们可以对一个值进行一些转换,但在这些转换完成后,变量将是不可变的。 + +`mut` 和变量遮蔽之间的另一个区别是,因为我们在再次使用 `let` 关键字时有效地创建了一个新的变量,所以我们可以改变值的类型,但重复使用相同的名称。例如,假设我们程序要求用户输入空格字符来显示他们想要的空格数目,但我们实际上想要将该输入存储为一个数字: + +```rust +{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-04-shadowing-can-change-types/src/main.rs:here}} +``` + +这种结构是允许的,因为第一个 `spaces` 变量是一个字符串类型,第二个 `spaces` 变量是一个全新的变量且和第第一个具有相同的变量名,且是一个数字类型。所以变量遮蔽可以让我们就不必给出不同的名称,如 `spaces_str` 和 `spaces_num`;相反我们可以重复使用更简单的 `spaces` 变量名。然而,如果我们对此尝试使用 `mut`,如下所示,我们将得到一个编译期错误: + +```rust,ignore,does_not_compile +{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-05-mut-cant-change-types/src/main.rs:here}} +``` + +该错误表明我们不允许更改变量的类型: + +```console +{{#include ../listings/ch03-common-programming-concepts/no-listing-05-mut-cant-change-types/output.txt}} +``` \ No newline at end of file diff --git a/src/intro.md b/src/intro.md new file mode 100644 index 00000000..3548b9ce --- /dev/null +++ b/src/intro.md @@ -0,0 +1,18 @@ +# 为何又多了一本书? + +与国外的Rust发展如火如荼相比,国内的近况不是特别理想。 + +导致目前这种状况的原因,我认为有: +1. 上手难度大,学习曲线陡峭 +2. 中文资料少,英文资料难学(基于原因1) +3. 没有体系化的学习路线,新人往往扫完一遍入门书籍,就不知道何去何从 +4. 缺少一个活跃的社区 + +因此我联合几个朋友一起创建了Rust中文社区(Rust Cn Members), + +## 本书的目标读者 + +目标用户分为三类: +1. 有其它语言编程基础,无Rust编程语言经验的爱好者 +2. 已经熟悉Rust想要更进一步的中级Rust程序员 +3. 想要随时检索一些Rust知识和代码,对生产力有要求的Rust开发者 \ No newline at end of file diff --git a/src/style-guide/intro.md b/src/style-guide/intro.md index ff247df6..07d8088f 100644 --- a/src/style-guide/intro.md +++ b/src/style-guide/intro.md @@ -1,230 +1 @@ -# 命名规范 - -基本的Rust命名规范在[RFC 430]中有描述. - -通常,对于"type-level"的构造Rust倾向于使用驼峰命名,而对于'value-level'的构造使用蛇形命名。详情如下: - -| 条目 | 惯例 | -| ---- | ---------- | -| 包Crates | [unclear](https://github.com/rust-lang/api-guidelines/issues/29) | -| 模块Modules | `snake_case` | -| 类型Types | `UpperCamelCase` | -| 特征Traits | `UpperCamelCase` | -| 枚举变量Enum variants | `UpperCamelCase` | -| 函数Functions | `snake_case` | -| 方法Methods | `snake_case` | -| 通用构造器General constructors | `new` or `with_more_details` | -| 转换构造器Conversion constructors | `from_some_other_type` | -| 宏Macros | `snake_case!` | -| 局部变量Local variables | `snake_case` | -| 静态类型Statics | `SCREAMING_SNAKE_CASE` | -| 常量Constants | `SCREAMING_SNAKE_CASE` | -| 类型参数Type parameters | `UpperCamelCase`, 通常使用一个大写字母: `T` | -| 生命周期Lifetimes | 通常使用小写字母: `'a`, `'de`, `'src` | -| Features | [unclear](https://github.com/rust-lang/api-guidelines/issues/101) but see [C-FEATURE] | - -对于驼峰命名法, 复合词的缩略形式我们认为是一个单独的词语,所以只对首字母进行大写: 使用`Uuid`而不是`UUID`, `Usize`而不是`USize`, `Stdin`而不是`StdIn`.对于蛇形命名法,缩略词用全小写: `is_xid_start`. - -对于蛇形命名(包括全大写的`SCREAMING_SNAKE_CASE`), 除了最后一部分,其它部分的词语都不能由单个字母组成: -`btree_map`而不是`b_tree_map`, `PI_2`而不是`PI2`. - -包名不应该使用`-rs`或者`-rust`作为后缀,因为每一个包都是Rust写的,因此这种多余的注释其实没有任何意义。 - -[RFC 430]: https://github.com/rust-lang/rfcs/blob/master/text/0430-finalizing-naming-conventions.md -[C-FEATURE]: #c-feature - - -## 类型转换要遵守`as_`, `to_`, `into_`命名惯例(C-CONV) -类型转换应该通过方法调用的方式实现,其中的前缀规则如下: - -| 方法前缀 | 性能开销 | 所有权改变 | -| ------ | ---- | --------- | -| `as_` | Free | borrowed -\> borrowed | -| `to_` | Expensive | borrowed -\> borrowed
borrowed -\> owned (non-Copy types)
owned -\> owned (Copy types) | -| `into_` | Variable | owned -\> owned (non-Copy types) | - -For example: - -- [`str::as_bytes()`] 把`str`变成UTF-8字节数组, 性能开销是0. 其中输入是一个借用的`&str`,输出也是一个借用的`&str`. -- [`Path::to_str`] 会执行一次昂贵的UTF-8字节数组检查,输入和输出都是借用的。对于这种情况,如果把方法命名为`as_str`是不正确的,因为这个方法的开销还挺大. -- [`str::to_lowercase()`]在调用过程中会遍历字符串的字符,且可能会分配新的内存对象.输入是一个借用的`str`,输出是一个有独立所有权的`String` -- [`String::into_bytes()`]返回`String`底层的`Vec`数组,转换本身是零消耗的。该方法获取`String`的所有权,然后返回一个新的有独立所有权的`Vec` - - -[`str::as_bytes()`]: https://doc.rust-lang.org/std/primitive.str.html#method.as_bytes -[`Path::to_str`]: https://doc.rust-lang.org/std/path/struct.Path.html#method.to_str -[`str::to_lowercase()`]: https://doc.rust-lang.org/std/primitive.str.html#method.to_lowercase -[`f64::to_radians()`]: https://doc.rust-lang.org/std/primitive.f64.html#method.to_radians -[`String::into_bytes()`]: https://doc.rust-lang.org/std/string/struct.String.html#method.into_bytes -[`BufReader::into_inner()`]: https://doc.rust-lang.org/std/io/struct.BufReader.html#method.into_inner -[`BufWriter::into_inner()`]: https://doc.rust-lang.org/std/io/struct.BufWriter.html#method.into_inner - - -当一个单独的值被某个类型所包装时,访问该类型的内部值应通过`into_inner()`方法来访问。例如将一个缓冲区值包装为[`BufReader`]类型,还有[`GzDecoder`]、[`AtomicBool`]等,都是这种类型。 - - -[`BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html#method.into_inner -[`GzDecoder`]: https://docs.rs/flate2/0.2.19/flate2/read/struct.GzDecoder.html#method.into_inner -[`AtomicBool`]: https://doc.rust-lang.org/std/sync/atomic/struct.AtomicBool.html#method.into_inner - -如果`mut`限定符在返回类型中出现,那么在命名上也应该体现出来。例如,[`Vec::as_mut_slice`] 就说明它返回了一个mut切片,在这种情况下`as_mut_slice`比`as_slice_mut`更适合。 - -[`Vec::as_mut_slice`]: https://doc.rust-lang.org/std/vec/struct.Vec.html#method.as_mut_slice - -```rust -// 返回类型是一个mut切片. -fn as_mut_slice(&mut self) -> &mut [T]; -``` - -##### 标准库中的一些例子 - -- [`Result::as_ref`](https://doc.rust-lang.org/std/result/enum.Result.html#method.as_ref) -- [`RefCell::as_ptr`](https://doc.rust-lang.org/std/cell/struct.RefCell.html#method.as_ptr) -- [`slice::to_vec`](https://doc.rust-lang.org/std/primitive.slice.html#method.to_vec) -- [`Option::into_iter`](https://doc.rust-lang.org/std/option/enum.Option.html#method.into_iter) - - -## 读访问器(Getter)的名称遵循Rust的命名规范(C-GETTER) - -除了少数例外,在Rust代码中`get`前缀不用于getter。 - -```rust -pub struct S { - first: First, - second: Second, -} - -impl S { - // 而不是get_first - pub fn first(&self) -> &First { - &self.first - } - - // 而不是get_first_mut, get_mut_first, or mut_first. - pub fn first_mut(&mut self) -> &mut First { - &mut self.first - } -} -``` -至于上文提到的少数例外,如下:当有且仅有一个值能被getter所获取时,才使用`get`前缀。例如, -[`Cell::get`]能直接访问到`Cell`中的内容。 - -[`Cell::get`]: https://doc.rust-lang.org/std/cell/struct.Cell.html#method.get - -有些getter会在过程中执行运行时检查,那么我们就可以考虑添加`_unchecked`getter函数,这个函数虽然不安全,但是往往具有更高的性能, -典型的例子如下: - -```rust -fn get(&self, index: K) -> Option<&V>; -fn get_mut(&mut self, index: K) -> Option<&mut V>; -unsafe fn get_unchecked(&self, index: K) -> &V; -unsafe fn get_unchecked_mut(&mut self, index: K) -> &mut V; -``` - -[`TempDir::path`]: https://docs.rs/tempdir/0.3.5/tempdir/struct.TempDir.html#method.path -[`TempDir::into_path`]: https://docs.rs/tempdir/0.3.5/tempdir/struct.TempDir.html#method.into_path - -### 标准库示例 - -- [`std::io::Cursor::get_mut`](https://doc.rust-lang.org/std/io/struct.Cursor.html#method.get_mut) -- [`std::ptr::Unique::get_mut`](https://doc.rust-lang.org/std/ptr/struct.Unique.html#method.get_mut) -- [`std::sync::PoisonError::get_mut`](https://doc.rust-lang.org/std/sync/struct.PoisonError.html#method.get_mut) -- [`std::sync::atomic::AtomicBool::get_mut`](https://doc.rust-lang.org/std/sync/atomic/struct.AtomicBool.html#method.get_mut) -- [`std::collections::hash_map::OccupiedEntry::get_mut`](https://doc.rust-lang.org/std/collections/hash_map/struct.OccupiedEntry.html#method.get_mut) -- [`<[T]>::get_unchecked`](https://doc.rust-lang.org/std/primitive.slice.html#method.get_unchecked) - -## 一个集合上的方法,如果返回迭代器,需遵循命名规则:`iter`, `iter_mut`, `into_iter` (C-ITER) - -```rust -fn iter(&self) -> Iter // Iter implements Iterator -fn iter_mut(&mut self) -> IterMut // IterMut implements Iterator -fn into_iter(self) -> IntoIter // IntoIter implements Iterator -``` -上面的规则适用于同构性的数据集合。与之相反,`str`类型是一个utf8字节数组切片,与同构性集合有一点微妙的差别,它可以认为是字节集合,也可以认为是字符集合,因此它提供了[`str::bytes`]去遍历字节,还有[`str::chars`]去遍历字符,而并没有直接定义`iter`等方法。 - -[`str::bytes`]: https://doc.rust-lang.org/std/primitive.str.html#method.bytes -[`str::chars`]: https://doc.rust-lang.org/std/primitive.str.html#method.chars - -上述规则只适用于方法,并不适用于函数。例如`url`包的[`percent_encode`]函数返回一个迭代器用于遍历百分比编码([Percent encoding](https://en.wikipedia.org/wiki/Percent-encoding))的字符串片段. 在这种情况下,使用`iter`/`iter_mut`/`into_iter`诸如此类的函数命名无法表达任何具体的含义。 - -[`percent_encode`]: https://docs.rs/url/1.4.0/url/percent_encoding/fn.percent_encode.html -[RFC 199]: https://github.com/rust-lang/rfcs/blob/master/text/0199-ownership-variants.md - -### 标准库示例 - -- [`Vec::iter`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.iter) -- [`Vec::iter_mut`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.iter_mut) -- [`Vec::into_iter`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.into_iter) -- [`BTreeMap::iter`](https://doc.rust-lang.org/std/collections/struct.BTreeMap.html#method.iter) -- [`BTreeMap::iter_mut`](https://doc.rust-lang.org/std/collections/struct.BTreeMap.html#method.iter_mut) - -## 迭代器的类型应该与产生它的方法名相匹配(C-ITER-TY) -例如形如`into_iter()`的方法应该返回一个`IntoIter`类型,与之相似,其它任何返回迭代器的方法也应该遵循这种命名惯例。 - -上述规则主要应用于方法,但是经常对于函数也适用。例如上文提到的url包中的[`percent_encode`]函数,返回了一个[`PercentEncode`]类型. - -[PercentEncode-type]: https://docs.rs/url/1.4.0/url/percent_encoding/struct.PercentEncode.html - -特别是,当这些类型跟包名前缀一起使用时,将具备非常清晰的含义,例如[`vec::IntoIter`]. - -[`vec::IntoIter`]: https://doc.rust-lang.org/std/vec/struct.IntoIter.html - -### 标准库示例 - -* [`Vec::iter`] returns [`Iter`][slice::Iter] -* [`Vec::iter_mut`] returns [`IterMut`][slice::IterMut] -* [`Vec::into_iter`] returns [`IntoIter`][vec::IntoIter] -* [`BTreeMap::keys`] returns [`Keys`][btree_map::Keys] -* [`BTreeMap::values`] returns [`Values`][btree_map::Values] - -[`Vec::iter`]: https://doc.rust-lang.org/std/vec/struct.Vec.html#method.iter -[slice::Iter]: https://doc.rust-lang.org/std/slice/struct.Iter.html -[`Vec::iter_mut`]: https://doc.rust-lang.org/std/vec/struct.Vec.html#method.iter_mut -[slice::IterMut]: https://doc.rust-lang.org/std/slice/struct.IterMut.html -[`Vec::into_iter`]: https://doc.rust-lang.org/std/vec/struct.Vec.html#method.into_iter -[vec::IntoIter]: https://doc.rust-lang.org/std/vec/struct.IntoIter.html -[`BTreeMap::keys`]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html#method.keys -[btree_map::Keys]: https://doc.rust-lang.org/std/collections/btree_map/struct.Keys.html -[`BTreeMap::values`]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html#method.values -[btree_map::Values]: https://doc.rust-lang.org/std/collections/btree_map/struct.Values.html - - - -## Cargo Feature的名称不应该包含占位词(C-FEATURE) - -不要在[Cargo feature]中包含无法传达任何意义的词,例如`use-abc`或`with-abc`,直接命名为`abc`即可。 - -[Cargo feature]: http://doc.crates.io/manifest.html#the-features-section - -一个典型的例子就是:一个包对标准库有可选性的依赖。标准的写法如下: - -```toml -# 在Cargo.toml中 - -[features] -default = ["std"] -std = [] -``` - -```rust -// 在我们自定义的lib.rs中 - -#![cfg_attr(not(feature = "std"), no_std)] -``` -除了`std`之外,不要使用任何`ust-std`或者`with-std`等自以为很有创造性的名称。 - -## 命名要使用一致性的词序(C-WORD-ORDER) - -这是一些标准库中的错误类型: - -- [`JoinPathsError`](https://doc.rust-lang.org/std/env/struct.JoinPathsError.html) -- [`ParseBoolError`](https://doc.rust-lang.org/std/str/struct.ParseBoolError.html) -- [`ParseCharError`](https://doc.rust-lang.org/std/char/struct.ParseCharError.html) -- [`ParseFloatError`](https://doc.rust-lang.org/std/num/struct.ParseFloatError.html) -- [`ParseIntError`](https://doc.rust-lang.org/std/num/struct.ParseIntError.html) -- [`RecvTimeoutError`](https://doc.rust-lang.org/std/sync/mpsc/enum.RecvTimeoutError.html) -- [`StripPrefixError`](https://doc.rust-lang.org/std/path/struct.StripPrefixError.html) - -它们都使用了`谓语-宾语-错误`的词序,如果我们想要表达一个网络地址无法分析的错误,由于词序一致性的原则,命名应该如下`ParseAddrError`,而不是`AddrParseError`。 - -词序和个人习惯有很大关系,想要注意的是,你可以选择合适的词序,但是要在包的范畴内保持一致性,就如标准库中的包一样。 +# Rust代码风格 \ No newline at end of file diff --git a/src/why-another.md b/src/why-another.md index 10b5e5d7..f809d34b 100644 --- a/src/why-another.md +++ b/src/why-another.md @@ -1,11 +1 @@ -# 为何又多了一本书? - -与国外的Rust发展如火如荼相比,国内的近况不是特别理想。 - -导致目前这种状况的原因,我认为有: -1. 上手难度大,学习曲线陡峭 -2. 中文资料少,英文资料难学(基于原因1) -3. 没有体系化的学习路线,新人往往扫完一遍入门书籍,就不知道何去何从 -4. 缺少一个活跃的社区 - -因此我联合几个朋友一起创建了Rust中文社区(Rust Cn Members), \ No newline at end of file +# 为何创作本书