diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 468b1b82..817979ff 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -36,6 +36,9 @@ - [模式适用场景](basic/match-pattern/pattern-match.md) - [全模式列表](basic/match-pattern/all-patterns.md) - [方法Method](basic/method.md) + - [集合类型 todo](basic/collections/intro.md) + - [动态数组Vector todo](basic/collections/vector.md) + - [KV存储HashMap todo](basic/collections/hashmap.md) - [泛型和特征](basic/trait/intro.md) - [泛型Generics](basic/trait/generic.md) - [特征Trait](basic/trait/trait.md) @@ -48,14 +51,13 @@ - [Rust高级进阶 doing](advance/intro.md) + - [生命周期(todo)](advance/lifetime/intro.md) + - [认识生命周期](advance/lifetime/basic.md) + - [深入生命周期](advance/lifetime/advance.md) - [自定义类型和动态类型 todo](advance/custom-type.md) - - [集合类型(todo)](advance/collection.md) - [格式化输出(todo)](advance/formatted-output.md) - [文档注释(todo)](advance/comment.md) - [包和模块(todo)](advance/crate-module.md) - - [生命周期(todo)](advance/lifetime/intro.md) - - [认识生命周期](advance/lifetime/basic.md) - - [深入生命周期](advance/lifetime/advance.md) - [迭代器(todo)](advance/interator.md) - [函数式编程(todo)](advance/functional-programing.md) - [智能指针(todo)](advance/smart-pointer.md) @@ -66,7 +68,7 @@ - [Rust最佳实践 todo](practice/intro.md) - [一些写代码的技巧 todo](practice/coding-tips.md) - [最佳实践 todo](practice/best-pratice.md) - + - [错误处理 todo](errors/intro.md) - [简化错误处理](errors/simplify.md) - [自定义错误](errors/user-define.md) diff --git a/src/advance/collection.md b/src/advance/collection.md deleted file mode 100644 index 9ceffe5c..00000000 --- a/src/advance/collection.md +++ /dev/null @@ -1 +0,0 @@ -# collection.md \ No newline at end of file diff --git a/src/basic/collections/hashmap.md b/src/basic/collections/hashmap.md new file mode 100644 index 00000000..eb2019e3 --- /dev/null +++ b/src/basic/collections/hashmap.md @@ -0,0 +1 @@ +# KV存储HashMap todo diff --git a/src/basic/collections/intro.md b/src/basic/collections/intro.md new file mode 100644 index 00000000..c57eee83 --- /dev/null +++ b/src/basic/collections/intro.md @@ -0,0 +1 @@ +# 集合类型 todo diff --git a/src/basic/collections/vector.md b/src/basic/collections/vector.md new file mode 100644 index 00000000..afecf47d --- /dev/null +++ b/src/basic/collections/vector.md @@ -0,0 +1 @@ +# 动态数组Vector todo diff --git a/src/basic/compound-type/enum.md b/src/basic/compound-type/enum.md index 5fea9c51..b3fecd57 100644 --- a/src/basic/compound-type/enum.md +++ b/src/basic/compound-type/enum.md @@ -14,10 +14,10 @@ enum PokerSuit { 再回到之前创建的`PokerSuit`,扑克总共有四种花色,而这里我们枚举出所有的可能值,这也正是`枚举`名称的由来。 -任何一张扑克,它的花色肯定会落在四种花色中,而且也只会落在其中一个花色上,这种特性非常适合枚举的使用,因为**`枚举值`**只可能是其中一个成员。抽象来看,四种花色尽管是不同的花色,但是它们都是扑克花色这个概念,因此当某个函数处理扑克花色时,可以把它们当作相同的类型进行传参。 +任何一张扑克,它的花色肯定会落在四种花色中,而且也只会落在其中一个花色上,这种特性非常适合枚举的使用,因为**枚举值**只可能是其中一个成员。抽象来看,四种花色尽管是不同的花色,但是它们都是扑克花色这个概念,因此当某个函数处理扑克花色时,可以把它们当作相同的类型进行传参。 细心的读者应该注意到,我们对之前的`枚举类型`和`枚举值`进行了重点标注,这是因为对于新人来说容易混淆相应的概念,总而言之: -**枚举类型是一个类型,它会包含所有可能的枚举成员, 而枚举值是该类型中的具体某个成员的实现。** +**枚举类型是一个类型,它会包含所有可能的枚举成员, 而枚举值是该类型中的具体某个成员的实例。** ## 枚举值 现在来创建`PokerSuit`枚举类型的两个成员实例: @@ -89,7 +89,7 @@ fn main() { 直接将数据信息关联到枚举成员上,直接省去近一半的代码,这种实现漂亮不? -而且不仅仅如此,同一个枚举类型下的不同成员还能持有不同的类型,例如让部分花色打印1-13的字样,另外花色打印上A-K的字样: +而且不仅仅如此,同一个枚举类型下的不同成员还能持有不同的类型,例如让部分花色打印`1-13`的字样,另外花色打印上`A-K`的字样: ```rust enum PokerCard { Clubs(u8), @@ -124,7 +124,7 @@ enum IpAddr { ``` 该例子跟我们之前的扑克牌很像,只不过枚举成员包含的类型更复杂了,变成了结构体:分别通过`Ipv4Addr`和`Ipv4Addr`来定义两种不同的IP数据。 -从这些例子可以看出,**任何类型的数据都可以放入枚举成员中**: 例例如字符串、数值、结构体甚至另一个枚举。 +从这些例子可以看出,**任何类型的数据都可以放入枚举成员中**: 例如字符串、数值、结构体甚至另一个枚举。 增加一些挑战?先看以下代码: ```rust @@ -165,9 +165,9 @@ struct ChangeColorMessage(i32, i32, i32); // 元组结构体 ## 同一化类型 -最后,再用一个实际项目中的设计考虑,来结束枚举类型的语法学习。 +最后,再用一个实际项目中的简化片段,来结束枚举类型的语法学习。 -例如我们有一个web服务,需要接受用户的长连接,假设连接有两种:TcpStream和TlsStream,但是我们希望对这两个连接的处理流程相同,也就是用同一个函数来处理这两个连接,代码如下: +例如我们有一个web服务,需要接受用户的长连接,假设连接有两种:`TcpStream`和`TlsStream`,但是我们希望对这两个连接的处理流程相同,也就是用同一个函数来处理这两个连接,代码如下: ```rust func new (stream: TcpStream) { let mut s = stream; @@ -191,14 +191,15 @@ enum Websocket { ``` ## Option枚举用于处理空值 -在其它编程语言中,往往都有一个`null`关键字,该关键字用于表明一个变量当前的值为空(不是零值,例如整形的零值是0),也就是不存在值。当你对这些`null`进行操作时,例如调用一个方法,就会直接抛出异常,导致程序的崩溃,因此我们在编程时需要格外的小心去处理这些`null`空值。 +在其它编程语言中,往往都有一个`null`关键字,该关键字用于表明一个变量当前的值为空(不是零值,例如整形的零值是0),也就是不存在值。当你对这些`null`进行操作时,例如调用一个方法,就会直接抛出null异常,导致程序的崩溃,因此我们在编程时需要格外的小心去处理这些`null`空值。 > Tony Hoare,null的发明者,曾经说过有非常有名的话 > > 我称之为我十亿美元的错误。当时,我在使用一个面向对象语言设计第一个综合性的面向引用的类型系统。我的目标是通过编译器的自动检查来保证所有引用的使用都应该是绝对安全的。不过在设计过程中,我未能抵抗住诱惑,引入了空引用的概念,因为它非常容易实现。就是因为这个决策,引发了无数错误、漏洞和系统崩溃,在之后的四十多年中造成了数十亿美元的苦痛和伤害。 -然后空值的表达依然非常有意义,因为空值表示当前时刻变量的值是缺失的。因此,Rust吸取了众多教训,决定抛弃`null`,而改为使用`Option`枚举变量来表述这种结果: -**一个变量要么有值:`Some(T)`, 要么为空: `None`,定义如下: +尽管如此,空值的表达依然非常有意义,因为空值表示当前时刻变量的值是缺失的。有鉴于此,Rust吸取了众多教训,决定抛弃`null`,而改为使用`Option`枚举变量来表述这种结果。 + +`Option`枚举包含两个成员,一个成员表示含有值:`Some(T)`, 另一个表示没有值: `None`,定义如下: ```rust enum Option { Some(T), @@ -208,7 +209,7 @@ enum Option { 其中`T`是泛型参数,`Some(T)`表示该枚举成员的数据类型是`T`, 换句话说,`Some`可以包含任何类型的数据。 -`Option` 枚举是如此有用以至于它甚至被包含在了`prelude`(Rust会将最常用的类型、函数等提前引入进来,避免我们再手动引入)之中,你不需要将其显式引入作用域。另外,它的成员也是如此,可以不需要`Option::`前缀来直接使用`Some` 和 `None`。即便如此`Option` 也仍是常规的枚举,`Some(T)` 和 `None` 仍是 `Option` 的成员。 +`Option` 枚举是如此有用以至于它甚至被包含在了`prelude`(Rust会将最常用的类型、函数等提前引入进来,避免我们再手动引入)之中,你不需要将其显式引入作用域。另外,它的成员也是如此,无需使用`Option::`前缀就可直接使用`Some` 和 `None`。总之,不能因为`Some(T)`和`None`中没有`Option::`的身影,就否认它们是`Option`下的卧龙凤雏。 再来看以下代码: ```rust @@ -255,7 +256,7 @@ not satisfied 总的来说,为了使用 `Option` 值,需要编写处理每个成员的代码。你想要一些代码只当拥有 `Some(T)` 值时运行,允许这些代码使用其中的 `T`。也希望一些代码在值为 `None` 时运行,这些代码并没有一个可用的 `T` 值。`match` 表达式就是这么一个处理枚举的控制流结构:它会根据枚举的成员运行不同的代码,这些代码可以使用匹配到的值中的数据。 -这里先简单看一下`match`的大致模样,在[模式匹配](../match-pattern.md)中,我们会详细讲解: +这里先简单看一下`match`的大致模样,在[模式匹配](../match-pattern/intro.md)中,我们会详细讲解: ```rust fn plus_one(x: Option) -> Option { diff --git a/src/basic/compound-type/string-slice.md b/src/basic/compound-type/string-slice.md index 93b8af0f..c2c4d79a 100644 --- a/src/basic/compound-type/string-slice.md +++ b/src/basic/compound-type/string-slice.md @@ -121,7 +121,7 @@ error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immuta 回忆一下借用的规则:当我们已经有了可变借用时,就无法再拥有不可变的借用。因为`clear`需要清空改变`String`,因此它需要一个可变借用,而之后的`println!`又使用了不可变借用,因此编译无法通过。 -从上述代码可以看出,Rust不仅让我们的`api`更加容易使用,而且也在编译器就位我们消除了大量错误! +从上述代码可以看出,Rust不仅让我们的`api`更加容易使用,而且也在编译期就消除了大量错误! #### 其它切片 因为切片是对集合的部分引用,因此不仅仅字符串有切片,其它集合类型也有,例如数组: @@ -132,7 +132,7 @@ let slice = &a[1..3]; assert_eq!(slice, &[2, 3]); ``` -该数组切片的类型是`&[i32]`,数组切片和字符串切片的工作方式是一样的,例如持有一个引用指向原始数组的某个元素和长度。对于集合类型,我们在[这一章](../../advance/collection.md)中有详细的介绍。 +该数组切片的类型是`&[i32]`,数组切片和字符串切片的工作方式是一样的,例如持有一个引用指向原始数组的某个元素和长度。 ## 字符串字面量是切片 @@ -146,7 +146,7 @@ let s = "Hello, world!"; ```rust let s: &str = "Hello, world!"; ``` -该切片指向了程序可执行文件中的某个点,这也是为什么字符串字面量是不可变的,因为`&str`时一个不可变引用。 +该切片指向了程序可执行文件中的某个点,这也是为什么字符串字面量是不可变的,因为`&str`是一个不可变引用。 了解完切片,可以进入本节的正题了。 @@ -204,16 +204,16 @@ fn main() { fn add(self, s: &str) -> String { ``` -因为该方法涉及到更复杂的特征功能,因此我们这里简单说明下,`self`是`String`类型的字符串`s1`,该函数说明,只能将&str类型的字符串切片添加到String类型的`s1`上,然后返回一个新的`String`类型,所以`let s3 = s1 + &s2;`就很好解释了,将`String`类型的`s1`与`&str`类型的`s2`进行相加,最终得到`String`类型的s3. +因为该方法涉及到更复杂的特征功能,因此我们这里简单说明下,`self`是`String`类型的字符串`s1`,该函数说明,只能将`&str`类型的字符串切片添加到`String`类型的`s1`上,然后返回一个新的`String`类型,所以`let s3 = s1 + &s2;`就很好解释了,将`String`类型的`s1`与`&str`类型的`s2`进行相加,最终得到`String`类型的s3. 由此可推,以下代码也是合法的: ```rust - let s1 = String::from("tic"); - let s2 = String::from("tac"); - let s3 = String::from("toe"); +let s1 = String::from("tic"); +let s2 = String::from("tac"); +let s3 = String::from("toe"); - // String = String + &str + &str + &str + &str - let s = s1 + "-" + &s2 + "-" + &s3; +// String = String + &str + &str + &str + &str +let s = s1 + "-" + &s2 + "-" + &s3; ``` `String` + `&str`返回一个`String`,然后再继续跟一个`&str`进行`+`操作,返回一个`String`类型,不断循环,最终生成一个`s`,也是`String`类型。 @@ -240,7 +240,7 @@ fn say_hello(s: &str) { } ``` -实际上这种灵活用法是因为`deref`强制转换,具体我们会在[Deref特征](../../traits/deref.md)进行详细讲解。 +实际上这种灵活用法是因为`deref`隐式强制转换,具体我们会在[Deref特征](../../traits/deref.md)进行详细讲解。 ## 字符串索引 @@ -283,10 +283,10 @@ let hello = String::from("中国人"); 所以,可以看出来Rust提供了不同的字符串展现方式,这样程序可以挑选自己想要的方式去使用,而无需去管字符串从人类语言角度看长什么样。 -还有一个原因导致了Rust不允许去索引字符:因为索引操作,我们总是期望它的性能表现是O(1),然后对于`String`类型来说,无法保证这一点,因为Rust可能需要从0开始去遍历字符串来定位合法的字符。 +还有一个原因导致了Rust不允许去索引字符:因为索引操作,我们总是期望它的性能表现是O(1),然而对于`String`类型来说,无法保证这一点,因为Rust可能需要从0开始去遍历字符串来定位合法的字符。 ## 字符串切片 -前文提到过,字符串切片是非常危险的操作,因为切片的索引是通过字节来进行,但是字符串是UTF8编码,因此你无法保证索引的字节刚好落在字符的边界上,例如: +前文提到过,字符串切片是非常危险的操作,因为切片的索引是通过字节来进行,但是字符串又是UTF8编码,因此你无法保证索引的字节刚好落在字符的边界上,例如: ```rust let hello = "中国人";