Merge branch 'sunface:main' into main

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

@ -1,4 +1,4 @@
# Rust 语言圣经 (The course)
# Rust语言圣经 (The Course)
- 在线阅读
@ -14,7 +14,7 @@
### 教程简介
**`Rust 语言圣经`**涵盖从**入门到精通**所需的全部 Rust 知识,目录及内容都经过深思熟虑的设计,同时语言生动幽默,行文流畅自如,摆脱技术书籍常有的机器味和晦涩感。
**`Rust语言圣经`**涵盖从**入门到精通**所需的 Rust 知识,目录及内容都经过深思熟虑的设计,同时语言生动幽默,行文流畅自如,摆脱技术书籍常有的机器味和晦涩感。
在 Rust 基础教学的同时,我们还提供了(部分):

@ -1,4 +1,4 @@
# 同时运行多个Future
# 使用`join!`和`select!`同时运行多个Future
招数单一,杀伤力惊人,说的就是 `.await` ,但是光用它,还真做不到一招鲜吃遍天。比如我们该如何同时运行多个任务,而不是使用`.await`慢悠悠地排队完成。
## join!

@ -1,10 +1,10 @@
# 基本类型
当一门语言不谈类型时,你得小心,这大概率是动态语言(别拍我,我承认是废话)。但是把类型大张旗鼓的用多个章节去讲的Rust 是其中之一。
Rust每个值都有其确切的数据类型, 总的来说可以分为两类:基本类型和复合类型。 基本类型意味着它们往往是一个最小化原子类型,无法解构为其它类型(一般意义上来说),由以下组成:
Rust 每个值都有其确切的数据类型,总的来说可以分为两类:基本类型和复合类型。 基本类型意味着它们往往是一个最小化原子类型,无法解构为其它类型(一般意义上来说),由以下组成:
- 数值类型: 有符号整数 (`i8`, `i16`, `i32`, `i64`, `isize`)、 无符号整数 (`u8`, `u16`, `u32`, `u64`, `usize`) 、浮点数 (`f32`, `f64`)、以及有理数、复数
- 字符串:字符串字面量和字符串切片&str
- 字符串:字符串字面量和字符串切片 `&str`
- 布尔类型: `true`和`false`
- 字符类型: 表示单个Unicode字符存储为4个字节
- 元类型: 即 `()` ,其唯一的值也是 `()`
@ -12,9 +12,9 @@ Rust每个值都有其确切的数据类型, 总的来说可以分为两类:
## 类型推导与标注
`python`、`javascript`等动态语言不同Rust是一门静态类型语言,也就是编译器必须在编译期知道我们所有变量的类型,但这不意味着你需要为每个变量指定类型,因为**Rust编译器很聪明它可以根据变量的值和上下文中的使用方式来自动推导出变量的类型**,同时编译器也不够聪明,在某些情况下,它无法推导出变量类型,需要手动去给予一个类型标注,关于这一点在[Rust语言初印象](../../first-try/hello-world.md#Rust语言初印象)中有过展示.
`Python``Javascript` 等动态语言不同Rust 是一门静态类型语言,也就是编译器必须在编译期知道我们所有变量的类型,但这不意味着你需要为每个变量指定类型,因为**Rust 编译器很聪明,它可以根据变量的值和上下文中的使用方式来自动推导出变量的类型**,同时编译器也不够聪明,在某些情况下,它无法推导出变量类型,需要手动去给予一个类型标注,关于这一点在[Rust语言初印象](../../first-try/hello-world.md#Rust语言初印象)中有过展示
来看段代码:
来看段代码
```rust
let guess = "42".parse().expect("Not a number!");
```
@ -30,5 +30,5 @@ error[E0282]: type annotations needed
| ^^^^^ consider giving `guess` a type
```
因此我们需要提供给编译器更多的信息,例如给`guess`变量一个**显式的类型标注**: `let guess: i32 = ...` 或者`"42".parse::<i32>()`.
因此我们需要提供给编译器更多的信息,例如给 `guess` 变量一个**显式的类型标注** `let guess: i32 = ...` 或者 `"42".parse::<i32>()`

@ -1,5 +1,5 @@
# 数值类型
我朋友有一个领导(读者:你朋友?黑人问号)说过一句话所有代码就是0和1简单的很。咱不评价这句话的正确性但是计算机底层由01组成倒是真的。
我朋友有一个领导(读者:你朋友?黑人问号)说过一句话:所有代码就是 0 和 1 ,简单的很。咱不评价这句话的正确性,但是计算机底层由 01 组成倒是真的。
计算机和数值关联在一起的时间,远比我们想象的要长,因此数值类型可以说是有计算机以来就有的类型,下面内容将深入讨论 Rust 的数值类型以及相关的运算符。
@ -7,11 +7,11 @@
Rust 使用一个相对传统的语法来创建整数(`1`, `2`,...)和浮点数(`1.0`,`1.1`,...)。整数、浮点数的运算和你在其它语言上见过的一致,都是通过常见的运算符来完成。
> 不仅仅是数值类型Rust也允许在复杂类型上定义运算符例如在自定义类型上定义`+`运算符,这种行为被称为运算符重载, Rust具体支持的可重载运算符见[这里](../../appendix/operators.md#运算符)
> 不仅仅是数值类型Rust 也允许在复杂类型上定义运算符,例如在自定义类型上定义 `+` 运算符这种行为被称为运算符重载Rust 具体支持的可重载运算符见[这里](../../appendix/operators.md#运算符)
#### 整数类型
**整数**是没有小数部分的数字。之前使用过的`i32`类型, 表示有符号的32位整数`i` 是英文单词 *integer* 的首字母,与之相反的是 `u`,代表无符号 `unsigned` 类型)。下表显示了 Rust 中的内置的整数类型:
**整数**是没有小数部分的数字。之前使用过的 `i32` 类型,表示有符号的 32位 整数( `i` 是英文单词 *integer* 的首字母,与之相反的是 `u`,代表无符号 `unsigned` 类型)。下表显示了 Rust 中的内置的整数类型:
| 长度 | 有符号类型 | 无符号类型 |
@ -23,12 +23,12 @@ Rust使用一个相对传统的语法来创建整数(`1`,`2`,...)和浮点数(`1
| 128-位 | `i128` | `u128` |
| 视架构而定 | `isize` | `usize` |
类型定义的形式统一为:有无符号 + 类型大小(位数)。**无符号数**表示数字只能取正数,而**有符号**则表示数字即可以取正数又可以取负数。就像在纸上写数字一样:当要强调符号时,数字前面可以带上正号或负号;然而,当很明显确定数字为正数时,就不需要加上正号了。有符号数字以[补码](https://en.wikipedia.org/wiki/Two%27s_complement)形式存储。
类型定义的形式统一为:`有无符号 + 类型大小(位数)`。**无符号数**表示数字只能取正数,而**有符号**则表示数字即可以取正数又可以取负数。就像在纸上写数字一样:当要强调符号时,数字前面可以带上正号或负号;然而,当很明显确定数字为正数时,就不需要加上正号了。有符号数字以[补码](https://en.wikipedia.org/wiki/Two%27s_complement)形式存储。
每个有符号类型规定的数字范围是 -(2<sup>n - 1</sup>) ~ 2<sup>n -
1</sup> - 1其中 `n` 是该定义形式的位长度。因此 `i8` 可存储数字范围是 -(2<sup>7</sup>) ~ 2<sup>7</sup> - 1即 -128 ~ 127。无符号类型可以存储的数字范围是 0 ~ 2<sup>n</sup> - 1所以 `u8` 能够存储的数字为 0 ~ 2<sup>8</sup> - 1即 0 ~ 255。
此外,`isize` 和 `usize` 类型取决于程序运行的计算机cpu类型 若cpu是32位的则这两个类型是32位的同理若cpu是64位那么它们则是64位.
此外,`isize` 和 `usize` 类型取决于程序运行的计算机 CPU 类型: 若 CPU 是 32 位的,则这两个类型是 32 位的,同理,若 CPU 是 64 位,那么它们则是 64 位。
整形字面量可以用下表的形式书写:
@ -48,7 +48,7 @@ Rust使用一个相对传统的语法来创建整数(`1`,`2`,...)和浮点数(`1
>
> 比方说有一个 `u8` ,它可以存放从 0 到 255 的值。那么当你将其修改为范围之外的值,比如 256则会发生**整型溢出**。关于这一行为 Rust 有一些有趣的规则:当在 debug 模式编译时Rust 会检查整型溢出,若存在这些问题,则使程序在编译时 *panic*(崩溃,Rust 使用这个术语来表明程序因错误而退出)。
>
> 在当使用 `--release` 参数进行 release 模式构建时Rust **不**检测溢出。相反当检测到整型溢出时Rust 会按照补码循环溢出(*twos complement wrapping*)的规则处理。简而言之,大于该类型最大值的数值会被补码转换成该类型能够支持的对应数字的最小值。比如在 `u8` 的情况下256 变成 0257 变成 1依此类推。程序不会 panic但是该变量的值可能不是你期望的值。依赖这种默认行为的代码都应该被认为是错误的代码。
> 在当使用 `--release` 参数进行 release 模式构建时Rust **不**检测溢出。相反当检测到整型溢出时Rust 会按照补码循环溢出(*twos complement wrapping*)的规则处理。简而言之,大于该类型最大值的数值会被补码转换成该类型能够支持的对应数字的最小值。比如在 `u8` 的情况下256 变成 0257 变成 1依此类推。程序不会 *panic*,但是该变量的值可能不是你期望的值。依赖这种默认行为的代码都应该被认为是错误的代码。
>
> 要显式处理可能的溢出,可以使用标准库针对原始数字类型提供的这些方法:
>
@ -100,7 +100,7 @@ fn main() {
这些语句中的每个表达式都使用了数学运算符,并且计算结果为一个值,然后绑定到一个变量上。[附录](../../appendix/operators.md)中给出了 Rust 提供的所有运算符的列表。
再来看一个综合性的示例:
再来看一个综合性的示例
```rust
fn main() {
@ -138,9 +138,9 @@ println!("{:02}", forty_twos[0]);
浮点数类型是基于二进制实现的,但是我们想要计算的数字往往是基于十进制,例如 `0.1` 在二进制上并不存在精确的表达形式,但是在十进制上就存在。这种不匹配性导致一定的歧义性,更多的,虽然浮点数能代表真实的数值,但是由于底层格式问题,它往往受限于定长的浮点数精度,如果你想要表达完全精准的真实数字,只有使用无限精度的浮点数才行
2. **浮点数在某些特性上是反直觉的**
例如大家都会觉得浮点数可以进行比较,对吧?是的,它们确实可以使用`>`,`>=`等进行比较,但是在某些场景下,这种直觉上的比较特性反而会害了你。因为`f32`,`f64`上的比较运算实现的是`std::cmp::PartialEq`特征(类似其他语言的接口), 但是并没有实现`std::cmp::Eq`特征,但是后者在其它数值类型上都有定义,说了这么多,可能大家还是云里雾里,用一个例子来举例:
例如大家都会觉得浮点数可以进行比较,对吧?是的,它们确实可以使用 `>` `>=` 等进行比较,但是在某些场景下,这种直觉上的比较特性反而会害了你。因为 `f32` `f64` 上的比较运算实现的是 `std::cmp::PartialEq` 特征(类似其他语言的接口),但是并没有实现 `std::cmp::Eq` 特征,但是后者在其它数值类型上都有定义,说了这么多,可能大家还是云里雾里,用一个例子来举例:
Rust的HashMap数据结构是一个KV类型的hash map实现它对于`K`没有特定类型的限制,但是要求能用作`K`的类型必须实现了`std::cmp::Eq`特征,因此这意味着你无法使用浮点数作为`HashMap`的`Key`来存储键值对但是作为对比Rust的整数类型、字符串类型、布尔类型都实现了该特征因此可以作为`HashMap`的`Key`。
Rust`HashMap` 数据结构,是一个 KV 类型的 Hash Map 实现,它对于 `K` 没有特定类型的限制,但是要求能用作 `K` 的类型必须实现了 `std::cmp::Eq` 特征,因此这意味着你无法使用浮点数作为 `HashMap` `Key`来存储键值对但是作为对比Rust的整数类型、字符串类型、布尔类型都实现了该特征因此可以作为 `HashMap` `Key`
为了避免上面说的两个陷阱,你需要遵守以下准则:
- 避免在浮点数上测试相等性
@ -154,11 +154,11 @@ fn main() {
}
```
你可能以为这段代码没啥问题吧实际上它会panic(程序崩溃,抛出异常)因为二进制精度问题导致了0.1 + 0.2并不严格等于0.3它们可能在小数点N位后存在误差。
你可能以为,这段代码没啥问题吧,实际上它会 *panic*(程序崩溃,抛出异常),因为二进制精度问题,导致了 0.1 + 0.2 并不严格等于 0.3,它们可能在小数点 N 位后存在误差。
那如果非要进行比较呢?可以考虑用这种方式`(0.1 + 0.2 - 0.3).abs() < 0.00001`,.
那如果非要进行比较呢?可以考虑用这种方式 `(0.1 + 0.2 - 0.3).abs() < 0.00001` ,具体小于多少,取决于你对精度的需求。
讲到这里,相信大家基本已经明白了,为什么操作浮点数时要格外的小心,但是还不够,下面再来一段代码,直接震撼你的灵魂:
讲到这里,相信大家基本已经明白了,为什么操作浮点数时要格外的小心,但是还不够,下面再来一段代码,直接震撼你的灵魂
```rust
fn main() {
@ -197,15 +197,15 @@ note: run with `RUST_BACKTRACE=1` environment variable to display
➥a backtrace
```
仔细看,对`f32`类型做加法时,`0.1+0.2`的结果是`3e99999a`0.3也是`3e99999a`,因此`f32`下的`0.1+0.2==0.3`通过测试,但是到了`f64`类型时结果就不一样了因为f64精度高很多因此在小数点非常后面发生了一点微小的变化`0.1+0.2`以`4`结尾但是0.3以`3`结尾,这个细微区别导致`f64`下的测试失败了,并且抛出了异常。
仔细看,对 `f32` 类型做加法时,`0.1 + 0.2` 的结果是 `3e99999a``0.3` 也是 `3e99999a`,因此 `f32` 下的 `0.1 + 0.2 == 0.3` 通过测试,但是到了 `f64` 类型时,结果就不一样了,因为 `f64` 精度高很多,因此在小数点非常后面发生了一点微小的变化,`0.1 + 0.2` `4` 结尾,但是 `0.3` 以`3`结尾,这个细微区别导致 `f64` 下的测试失败了,并且抛出了异常。
是不是**blow your mind away**? 没关系,在本书的后续章节中类似的直击灵魂的地方还很多,这就是敢号称`Rust语言圣经`的底气!
是不是**blow your mind away**? 没关系,在本书的后续章节中类似的直击灵魂的地方还很多,这就是敢号称 `Rust语言圣经Rust Course` 的底气!
#### NaN
对于数学上未定义的结果,例如对负数取平方根 `-42.1.sqrt()` 会产生一个特殊的结果Rust 的浮点数类型使用 `NaN` (not a number)来处理这些情况。
**所有跟`NaN`交互的操作,都会返回一个`NaN`**,而且`NaN`不能用来比较,下面的代码会崩溃:
**所有跟 `NaN` 交互的操作,都会返回一个 `NaN`**,而且 `NaN` 不能用来比较,下面的代码会崩溃:
```rust
fn main() {
@ -214,7 +214,7 @@ fn main() {
}
```
出于防御性编程的考虑,可以使用`is_nan()`等方法,可以用来判断一个数值是否是`NaN`:
出于防御性编程的考虑,可以使用 `is_nan()` 等方法,可以用来判断一个数值是否是 `NaN`
```rust
fn main() {
@ -227,7 +227,8 @@ fn main() {
## 序列(Range)
Rust提供了一个非常简洁的方式用来生成连续的数值例如`1..5`生成从1到4的连续数字不包含5; `1..=5`生成从1到5的连续数字,包含5它的用途很简单常常用于循环中
Rust 提供了一个非常简洁的方式,用来生成连续的数值,例如 `1..5` ,生成从 1 到 4 的连续数字,不包含 5 `1..=5` ,生成从 1 到 5 的连续数字,包含 5 ,它的用途很简单,常常用于循环中:
```rust
for i in 1..=5 {
println!("{}",i);
@ -253,7 +254,7 @@ for i in 'a'..='z' {
## 有理数和复数
Rust的标准库相比其它语言对于准入的门槛较高因此有理数和复数并未包含在标准库中:
Rust 的标准库相比其它语言,对于准入的门槛较高,因此有理数和复数并未包含在标准库中
- 有理数和复数
- 任意大小的整数和任意精度的浮点数
- 固定精度的十进制小数,常用于货币相关的场景
@ -281,11 +282,11 @@ use num::complex::Complex;
## 总结
之前提到了过Rust的数值类型和运算跟其他语言较为相似但是实际上除了语法上的不同之外还是存在一些差异点:
之前提到了过 Rust 的数值类型和运算跟其他语言较为相似,但是实际上,除了语法上的不同之外,还是存在一些差异点
- **Rust 拥有相当多的数值类型**. 因此你需要熟悉这些类型所占用的字节数,这样就知道该类型允许的大小范围以及你选择的类型是否能表达负数
- **类型转换必须是显式的**. Rust 永远也不会偷偷把你的 16bit 整数转换成 32bit 整数
- **Rust的数值上可以使用方法**. 例如你可以用以下方法来将`13.14`取整: `13.14_f32.round()`, 在这里我们使用了类型后缀,因为编译器需要知道`13.14`的具体类型
- **Rust 的数值上可以使用方法**. 例如你可以用以下方法来将 `13.14` 取整: `13.14_f32.round()`在这里我们使用了类型后缀,因为编译器需要知道 `13.14 `的具体类型
数值类型的讲解已经基本结束,接下来,来看看字符和布尔类型。

@ -9,10 +9,10 @@
本章主要介绍 Rust 的基础语法、数据类型、项目结构等,学完本章,你将对 Rust 代码有一个清晰、完整的认识。
开始之前先通过一段代码来简单浏览下Rust的语法:
开始之前先通过一段代码来简单浏览下 Rust 的语法:
```rust
// rust程序入口函数跟其它语言一样都是main该函数目前无返回值
// Rust 程序入口函数,跟其它语言一样,都是 main该函数目前无返回值
fn main() {
// 使用let来声明变量进行绑定a是不可变的
// 此处没有指定a的类型编译器会默认根据a的值为a推断类型i32有符号32位整数
@ -43,8 +43,8 @@ fn main() {
```
> 注意
>在上面的`add`函数中,不要为`i+j`添加`;`,这会改变语法导致函数返回`()`而不是`i32`,具体参见[语句和表达式](./base-type/statement-expression.md)
>在上面的 `add` 函数中,不要为 `i+j` 添加 `;`,这会改变语法导致函数返回 `()` 而不是 `i32`具体参见[语句和表达式](./base-type/statement-expression.md)
有几点可以留意下:
- 字符串使用双引号`""`而不是单引号`''`, Rust中单引号是留给单个字符类型(`char`)使用的
- Rust使用`{}`来作为格式化输出占位符,其它语言可能使用的是`%s``%d`,`%p`等,由于`println!`会自动推导出具体的类型, 因此无需手动指定
有几点可以留意下
- 字符串使用双引号 `""` 而不是单引号 `''`Rust 中单引号是留给单个字符类型(`char`使用的
- Rust 使用 `{}` 来作为格式化输出占位符,其它语言可能使用的是 `%s``%d``%p` 等,由于 `println!` 会自动推导出具体的类型, 因此无需手动指定

@ -18,9 +18,9 @@
## 变量绑定
在其它语言中,我们用`var a = "hello world"`的方式给a赋值也就是把等式右边的`"hello world`"字符串赋值给变量`a`而在Rust中我们这样写`let a = "hello world"`, 同时给这个过程起了另一个名字:**变量绑定**。
在其它语言中,我们用 `var a = "hello world"` 的方式给 `a` 赋值,也就是把等式右边的 `"hello world"` 字符串赋值给变量 `a` ,而在 Rust 中,我们这样写: `let a = "hello world"` ,同时给这个过程起了另一个名字:**变量绑定**。
为何不用赋值而用绑定呢(其实你也可以称之为赋值,但是绑定的含义更清晰准确)?这里就涉及Rust最核心的原则-**所有权**,简单来讲,任何内存对象都是有主人的,而且一般情况下完全属于它的主人,绑定就是把这个对象绑定给一个变量,让这个变量成为它的主人(聪明的读者应该能猜到,在这种情况下,该对象之前的主人就会丧失对该对象的所有权),像极了我们的现实世界,不是吗?
为何不用赋值而用绑定呢(其实你也可以称之为赋值,但是绑定的含义更清晰准确)?这里就涉及 Rust 最核心的原则——**所有权**,简单来讲,任何内存对象都是有主人的,而且一般情况下完全属于它的主人,绑定就是把这个对象绑定给一个变量,让这个变量成为它的主人(聪明的读者应该能猜到,在这种情况下,该对象之前的主人就会丧失对该对象的所有权),像极了我们的现实世界,不是吗?
至于为何要采用所有权这种复杂的东东,先别急,等时机合适,我们会为你详细道来。
@ -41,7 +41,7 @@ fn main() {
}
```
保存文件,并使用 `cargo run`运行程序或者。你将会收到一条错误消息,输出如下所示:
保存文件,并使用 `cargo run` 运行程序,你将会收到一条错误消息,输出如下所示:
```console
$ cargo run
@ -67,7 +67,7 @@ error: aborting due to previous error
这种规则让我们的代码变得非常清晰,只有你想让你的变量改变时,它才能改变,这样就不会造成心智上的负担,也给别人阅读代码带来便利。
但是可变性也非常重要,否则我们就要像ClosureScript中那样每次要改变就是重新生成一个对象,在拥有大量对象的场景,性能会变得非常低下,内存拷贝的成本异常的高。
但是可变性也非常重要,否则我们就要像 ClosureScript 那样,每次要改变,就要重新生成一个对象,在拥有大量对象的场景,性能会变得非常低下,内存拷贝的成本异常的高。
在 Rust 中,可变性很简单,只要在变量名前加一个 `mut` 即可, 而且这种显式的声明方式还会给后来人传达这样的信息:嗯,这个变量在后面代码部分会发生改变。
@ -99,7 +99,7 @@ The value of x is: 6
### 变量解构
let表达式不仅仅用于变量的绑定还能进行复杂变量的解构从一个相对复杂的变量中匹配出该变量的一部分内容.
`let` 表达式不仅仅用于变量的绑定,还能进行复杂变量的解构:从一个相对复杂的变量中,匹配出该变量的一部分内容
```rust
fn main() {
@ -115,11 +115,12 @@ fn main() {
### 变量和常量之间的差异
变量的值不能更改可能让你想起其他另一个很多语言都有的编程概念:**常量***constant*)。与不可变变量一样,常量也是绑定到一个常量名且不允许更改的值,但是常量和变量之间存在一些差异
变量的值不能更改可能让你想起其他另一个很多语言都有的编程概念:**常量***constant*)。与不可变变量一样,常量也是绑定到一个常量名且不允许更改的值,但是常量和变量之间存在一些差异
首先,常量不允许使用 `mut`。**常量不仅仅默认不可变,而且自始至终不可变**。
- 常量不允许使用 `mut`。**常量不仅仅默认不可变,而且自始至终不可变**。
- 常量使用 `const` 关键字而不是 `let` 关键字来声明,并且值的类型**必须**标注。
常量使用 `const` 关键字而不是 `let` 关键字来声明,并且值的类型**必须**标注。我们将在下一节[数据类型](./base-type/index.md)中介绍,因此现在暂时无需关心细节。
我们将在下一节[数据类型](./base-type/index.md)中介绍,因此现在暂时无需关心细节。
下面是一个常量声明的例子,其常量名为 `MAX_POINTS`,值设置为 `100,000`Rust 常量的命名约定是全部字母都使用大写,并使用下划线分隔单词,另外对数字字面量可插入下划线以提高可读性):
@ -134,7 +135,7 @@ const MAX_POINTS: u32 = 100_000;
### 变量遮蔽(shadowing)
Rust允许声明相同的变量名,在后面声明的变量会遮蔽掉前面声明的, 如下所示:
Rust 允许声明相同的变量名,在后面声明的变量会遮蔽掉前面声明的,如下所示:
```rust
fn main() {
@ -162,10 +163,10 @@ The value of x in the inner scope is: 12
The value of x is: 6
```
这和`mut`变量的使用是不同的第二个let生成了完全不同的新变量两个变量只是恰好拥有同样的名称涉及一次内存对象的再分配
这和 `mut` 变量的使用是不同的,第二个 `let` 生成了完全不同的新变量,两个变量只是恰好拥有同样的名称,涉及一次内存对象的再分配
,而 `mut` 声明的变量,可以修改同一个内存地址上的值,并不会发生内存对象的再分配,性能要更好。
变量遮蔽的用处在于,如果你在某个作用域内无需再使用之前的变量(在被遮蔽后,无法再访问到之前的同名变量),就可以重复的使用变量名字,而不用绞尽脑汁去想更多的名字。
变量遮蔽的用处在于,如果你在某个作用域内无需再使用之前的变量(在被遮蔽后,无法再访问到之前的同名变量),就可以重复的使用变量名字,而不用绞尽脑汁去想更多的名字。
例如,假设有一个程序要统计一个空格字符串的空格数量:
```rust
@ -196,6 +197,6 @@ error[E0308]: mismatched types
error: aborting due to previous error
```
显然Rust对类型的要求很严格不允许将整数类型`usize`赋值给字符串类型,`usize`是一种cpu相关的整数类型在[数值类型](./base-type/numbers#整数类型)有详细介绍.
显然Rust 对类型的要求很严格,不允许将整数类型 `usize` 赋值给字符串类型。`usize` 是一种 CPU 相关的整数类型,在[数值类型](./base-type/numbers#整数类型)中有详细介绍。
万事开头难,到目前为止,都进展很顺利,那下面开始,咱们正式进入 Rust 的类型世界,看看有哪些挑战在前面等着大家。

@ -97,7 +97,7 @@ $ cargo check
Finished dev [unoptimized + debuginfo] target(s) in 0.06s
```
> Rust 虽然编译速度还行,但是还是不能 Go语言相提并论因为 Rust 需要做很多复杂的编译优化和语言特性解析, 甚至连如何优化编译速度都成了一门学问[优化编译速度](../compiler/speed-up.md)
> Rust 虽然编译速度还行,但是还是不能 Go 语言相提并论,因为 Rust 需要做很多复杂的编译优化和语言特性解析, 甚至连如何优化编译速度都成了一门学问[优化编译速度](../compiler/speed-up.md)
## Cargo.toml 和 Cargo.lock

@ -14,7 +14,7 @@
## 安装 VSCode 的 Rust 插件
在 VScode 的左侧扩展目录里,搜索 `rust`, 你能看到两个 Rust 插件,如果没有意外,这两个应该分别排名第一和第二:
在 VSCode 的左侧扩展目录里,搜索 `rust`, 你能看到两个 Rust 插件,如果没有意外,这两个应该分别排名第一和第二:
1. 官方的 `Rust`,作者是 `The Rust Programming Language`, 官方出品,牛逼就完了,但是……我们并不推荐,这个插件有几个问题:
- 首先是在代码跳转上支持的很烂,只能在自己的代码库中跳转,一旦跳到别的三方库,那就无法继续跳转,对于查看标准库和三方库的源码带来了极大的困扰
- 其次,不支持类型自动标注,对于 Rust 语言而言,类型说明是非常重要的,特别是在你不知道给变量一个什么类型时,这种 IDE 的自动提示就变得弥足珍贵

@ -149,7 +149,7 @@ Rust 语言表达能力更强,性能更高。同时线程安全方面 Rust 也
- 对于一般的用户edition 大版本的发布会告诉他们Rust 语言相比上次大版本发布,有了重大的改进,值得一看
- 对于 Rust 语言开发者,可以让他们的工作成果更快的被世人所知,不必锦衣夜行
好了,相信大家听了这么多 Rust 的优点,已经迫不及待想要开始学习旅程,那么容我引用一句 CS 的经典台词OK, let's go.
好了,相信大家听了这么多 Rust 的优点,已经迫不及待想要开始学习旅程,那么容我引用一句 CS 的经典台词Ok, Let's go.
## 总结

@ -4,3 +4,6 @@
https://www.reddit.com/r/rust/comments/rupcux/how_do_i_profile_a_rust_web_application_in/
https://zhuanlan.zhihu.com/p/191655266
## 内存对齐
https://www.reddit.com/r/rust/comments/s793x7/force_4byte_memory_alignment/

@ -28,5 +28,7 @@ https://www.reddit.com/r/rust/comments/rntx7s/why_use_boxleak/
## bounds check
https://www.reddit.com/r/rust/comments/rnbubh/whats_the_big_deal_with_bounds_checking/
https://www.reddit.com/r/rust/comments/s6u65e/optimization_of_bubble_sort_fails_without_hinting/
## 使用assert 优化检查性能
https://www.reddit.com/r/rust/comments/rui1zz/write_assertions_that_clarify_code_to_both_the/
Loading…
Cancel
Save