mirror of
https://github.com/KaiserY/trpl-zh-cn
synced 2024-11-09 08:51:18 +08:00
ch03-01
This commit is contained in:
parent
d42d8abc4b
commit
27712529bb
@ -4,10 +4,10 @@
|
||||
> <br>
|
||||
> commit 04aa3a45eb72855b34213703718f50a12a3eeec8
|
||||
|
||||
这一章涉及到几乎出现在所有编程语言中的概念,以及他们在 Rust 中是如何工作的。很多编程语言在核心概念上都是共通的。本章中展示的所有概念没有一个是 Rust 所特有的,不过我们会在 Rust 环境中讨论他们并解释他们的使用习惯。
|
||||
本章涉及一些几乎所有编程语言都有的概念,以及他们在 Rust 中是如何工作的。很多编程语言的核心概念都是共通的,本章中展示的概念都不是 Rust 特有的,不过我们会在 Rust 环境中讨论他们,解释他们的使用习惯。
|
||||
|
||||
具体的,我们将会学习变量,基本类型,函数,注释和控制流。这些基础知识将会出现在每一个 Rust 程序中,提早学习这些概念会使你在起步时拥有一个强有力的基础。
|
||||
具体的,我们将会学习变量,基本类型,函数,注释和控制流。这些基础知识将会出现在每一个 Rust 程序中,提早学习这些概念会使你拥有坚实的起步。
|
||||
|
||||
> ### 关键字
|
||||
>
|
||||
> Rust 语言有一系列被保留为只能被语言使用的**关键字**(*keywords*),如大部分语言一样。注意你不能使用这些关键字作为变量或函数的名称。大部分关键字有特殊的意义,并将会被用来进行 Rust 程序中的多种任务;一些关键字目前没有相关的功能不过为了将来可能添加进 Rust 的功能而被保留。可以在附录 A 中找到一份关键字的列表
|
||||
> Rust 语言有一系列保留的**关键字**(*keywords*),只能由语言本身使用,像大部分语言一样。你不能使用这些关键字作为变量或函数的名称,大部分关键字有特殊的意义,并被用来完成 Rust 程序中的各种任务;一些关键字目前没有分配,是为将来可能添加的功能保留的。可以在附录 A 中找到关键字的列表。
|
||||
|
@ -4,7 +4,7 @@
|
||||
> <br>
|
||||
> commit 04aa3a45eb72855b34213703718f50a12a3eeec8
|
||||
|
||||
第二章中提到过,变量默认是**不可变**(*immutable*)的。这是 Rust 中许多鼓励利用 Rust 提供的安全和简单并发优势编写代码的助力之一。不过,仍然有使变量可变的选项。让我们探索一下如何以及为什么鼓励你拥抱不可变性,还有为什么你可能想要弃之不用。
|
||||
第二章中提到过,变量默认是**不可变**(*immutable*)的。这是利用 Rust 安全和简单并发的优势编写代码一大助力。不过,变量仍然有可变的选项。让我们探索一下你如何以及为什么,应该拥抱不可变性,以及你为什么想要弃之不用。
|
||||
|
||||
当变量是不可变时,这意味着一旦一个值被绑定上了一个名称,你就不能改变这个值。作为说明,通过`cargo new --bin variables`在 *projects* 目录生成一个叫做 *variables* 的新项目。
|
||||
|
||||
@ -36,13 +36,13 @@ error[E0384]: re-assignment of immutable variable `x`
|
||||
| ^^^^^ re-assignment of immutable variable
|
||||
```
|
||||
|
||||
这个例子显示了编译器如何帮助你寻找程序中的错误。即便编译器错误可能是令人沮丧的,他们也仅仅意味着程序不能安全的完成你想让它完成的工作;他们**不能**说明你不是一个好的程序员!有经验的 Rustacean 们也会遇到编译器错误。这些错误表明错误的原因是`对不可变变量重新赋值`(`re-assignment of immutable variable`),因为我们尝试对不可变变量`x`赋第二个值。
|
||||
这个例子展示了编译器如何帮助你找出程序中的错误。即便编译错误令人沮丧,那也不过是说程序不能安全的完成你想让它完成的工作;而**不能**说明你是不是一个好程序员!有经验的 Rustacean 们一样会遇到编译错误。这些错误给出的原因是`对不可变变量重新赋值`(`re-assignment of immutable variable`),因为我们尝试对不可变变量`x`赋第二个值。
|
||||
|
||||
当尝试去改变之前设计为不可变的值出现编译时错误是很重要的,因为这种情况可能导致 bug。如果代码的一部分假设一个值永远也不会改变而另一部分代码改变了它,这样第一部分代码就有可能不能像它设计的那样运行。不得不承认这种 bug 难以跟踪,尤其是当第二部分代码只是**有时**当变量使不可变时。
|
||||
尝试去改变预设为不可变的值,产生编译错误是很重要的,因为这种情况可能导致 bug:如果代码的一部分假设一个值永远也不会改变,而另一部分代码改变了它,第一部分代码就有可能以不可预料的方式运行。不得不承认这种 bug 难以跟踪,尤其是第二部分代码只是**有时**改变其值。
|
||||
|
||||
Rust 编译器保证如果声明一个值不会改变,它就真的不会改变。这意味着当阅读和编写代码时,并不需要记录如何以及在哪可能会被改变,这使得代码易于推导。
|
||||
Rust 编译器保证,如果声明一个值不会变,它就真的不会变。这意味着当阅读和编写代码时,不需要厘清如何以及哪里可能会被改变,从而使得代码易于推导。
|
||||
|
||||
不过可变性也是非常有用的。变量只是默认不可变;可以通过在变量名之前增加`mut`来使其可变。它向之后的读者表明了其他部分的代码将会改变这个变量值的意图。
|
||||
不过可变性也是非常有用的。变量只是默认不可变;可以通过在变量名之前加 `mut` 来使其可变。它向读者表明了其他代码将会改变这个变量的意图。
|
||||
|
||||
例如,改变 *src/main.rs* 并替换其代码为如下:
|
||||
|
||||
@ -67,35 +67,35 @@ The value of x is: 5
|
||||
The value of x is: 6
|
||||
```
|
||||
|
||||
通过`mut`,允许把绑定到`x`的值从`5`改成`6`。在一些情况下,你会想要一个变量是可变的,因为这比只使用不可变变量实现的代码更易于编写。
|
||||
通过 `mut`,允许把绑定到 `x` 的值从 `5` 改成 `6`。在一些情况下,你会想要一个变量可变,因为相对不可变的风格更容易写。
|
||||
|
||||
除了避免 bug 外,这里还有多个需要权衡取舍的地方。例如,有时使用大型数据结构时,适当地使变量可变可能比复制和返回新分配的实例要更快。对于较小的数据结构,总是创建新实例并采用一种更函数式的编程风格可能会使代码更易理解。所以为了可读性而造成的性能惩罚也许是值得的。
|
||||
除了避免 bug 外,还有多处需要权衡取舍。例如,使用大型数据结构时,适当地使变量可变,可能比复制和返回新分配的实例更快。对于较小的数据结构,总是创建新实例,采用更偏向函数式的风格编程,可能会使代码更易理解,为可读性而遭受性能惩罚或许值得。
|
||||
|
||||
### 变量和常量的区别
|
||||
|
||||
不能改变一个变量的值可能会使你想起另一个大部分编程语言都有的概念:**常量**(*constants*)。类似于不可变变量,常量也是绑定到一个名称的不允许改变的值,不过常量与变量还是有一些区别。
|
||||
不允许改变值的变量,可能会使你想起另一个大部分编程语言都有的概念:**常量**(*constants*)。类似于不可变变量,常量也是绑定到一个名称的不允许改变的值,不过常量与变量还是有一些区别。
|
||||
|
||||
首先,不允许对常量使用`mut`:常量不光是默认不能改变,它总是不能改变。
|
||||
首先,不允许对常量使用 `mut`:常量不光默认不能变,它总是不能变。
|
||||
|
||||
常量使用`const`关键字而不是`let`关键字声明,而且*必须*注明值的类型。现在我们准备在下一部分,“数据类型”,涉及到类型和类型注解,所以现在无需担心这些细节,只需记住必须总是注明类型即可。
|
||||
声明常量使用 `const` 关键字而不是 `let`,而且*必须*注明值的类型。在下一部分,“数据类型”,涉及到类型和类型注解,现在无需关心这些细节,记住总是标注类型即可。
|
||||
|
||||
常量可以在任何作用域声明,包括全局作用域,这在一个值需要被很多部分的代码用到时很有用。
|
||||
|
||||
最后一个区别是常量只能用于常量表达式,而不能作为函数调用的结果或任何其他只在运行时计算的值。
|
||||
最后一个区别是常量只能用于常量表达式,而不能作为函数调用的结果,或任何其他只在运行时计算的值。
|
||||
|
||||
这是一个常量声明的例子,它的名称是`MAX_POINTS`而它的值是 100,000。(Rust 常量的命名规范是使用大写字母和单词间使用下划线):
|
||||
这是一个常量声明的例子,它的名称是 `MAX_POINTS`,值是 100,000。(常量使用下划线分隔的大写字母命名):
|
||||
|
||||
```rust
|
||||
const MAX_POINTS: u32 = 100_000;
|
||||
```
|
||||
|
||||
常量在整个程序生命周期中都有效,位于它声明的作用域之中。这使得常量可以用作多个部分的代码可能需要知道的程序范围的值,例如一个游戏中任何玩家可以获得的最高分或者光速。
|
||||
常量在整个程序生命周期中都有效,位于它声明的作用域之中。这使得常量可以作为多处代码使用的全局范围的值,例如一个游戏中所有玩家可以获取的最高分或者光速。
|
||||
|
||||
将用于整个程序的硬编码的值声明为为常量(并编写文档)对为将来代码维护者表明值的意义是很有用的。它也能帮助你将硬编码的值至于一处以便将来可能需要修改他们。
|
||||
将作用于整个程序的值,由硬编码改为常量(并编写文档),对后来的维护者了解值的意义很用帮助。它也能将硬编码的值汇总一处,为将来可能的修改提供方便。
|
||||
|
||||
### 覆盖(Shadowing)
|
||||
### 遮盖(Shadowing)
|
||||
|
||||
如第二章猜猜看游戏所讲到的,我们可以定义一个与之前变量名称相同的新变量,而新变量会**覆盖**之前的变量。Rustacean 们称其为第一个变量被第二个**给覆盖**了,这意味着第二个变量的值是使用这个变量时会看到的值。可以用相同变量名称来覆盖它自己以及重复使用`let`关键字来多次覆盖,如下所示:
|
||||
如第二章“猜猜看游戏”所讲的,我们可以定义一个与之前变量重名的新变量,而新变量会**遮盖**之前的变量。Rustacean 称之为“第一个变量被第二个**遮盖**了”,这意味着使用这个变量时会看第二个值。可以用相同变量名称来遮盖它自己,以及重复使用 `let` 关键字来多次遮盖,如下所示:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -111,7 +111,7 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
这个程序首先将`x`绑定到值`5`上。接着通过`let x =`覆盖`x`,获取原始值并加`1`这样`x`的值就变成`6`了。第三个`let`语句也覆盖了`x`,获取之前的值并乘以`2`,`x`的最终值是`12`。当运行这个程序,它会有如下输出:
|
||||
这个程序首先将 `x` 绑定到值 `5` 上。接着通过 `let x =` 遮盖 `x`,获取原始值并加 `1` 这样 `x` 的值就变成 `6` 了。第三个 `let` 语句也覆盖了 `x`,获取之前的值并乘以 `2`,`x` 最终的值是 `12`。运行这个程序,它会有如下输出:
|
||||
|
||||
```
|
||||
$ cargo run
|
||||
@ -120,22 +120,22 @@ $ cargo run
|
||||
The value of x is: 12
|
||||
```
|
||||
|
||||
这与将变量声明为`mut`是有区别的。因为除非再次使用`let`关键字,不小心尝试对变量重新赋值会导致编译时错误。我们可以用这个值进行一些计算,不过计算完之后变量仍然是不变的。
|
||||
这与将变量声明为 `mut` 是有区别的。因为除非再次使用 `let` 关键字,不小心尝试对变量重新赋值会导致编译时错误。我们可以用这个值进行一些计算,不过计算完之后变量仍然是不变的。
|
||||
|
||||
另一个`mut`与覆盖的区别是当再次使用`let`关键字时,事实上创建了一个新变量,我们可以改变值的类型。例如,假设程序请求用户输入空格来提供在一些文本之间需要多少空间来分隔,不过我们真正需要的是将输入存储成数字(多少个空格):
|
||||
`mut` 与遮盖的另一个区别是,当再次使用 `let` 时,实际上创建了一个新变量,我们可以改变值的类型。例如,假设程序请求用户输入空格来提供文本间隔,然而我们真正需要的是将输入存储成数字(多少个空格):
|
||||
|
||||
```rust
|
||||
let spaces = " ";
|
||||
let spaces = spaces.len();
|
||||
```
|
||||
这里允许第一个`spaces`变量是字符串类型,而第二个`spaces`变量,它是一个恰巧与第一个变量名字相同的崭新的变量,它是数字类型。因此覆盖使我们不必使用不同的名字,比如`spaces_str`和`spaces_num`;相反,我们可以复用`spaces`这个更简单的名称。然而,如果尝试使用`mut`,如下所示:
|
||||
这里允许第一个 `spaces` 变量是字符串类型,而第二个 `spaces` 变量,它是一个恰巧与第一个变量同名的崭新变量,是数字类型。遮盖使我们不必使用不同的名字,如 `spaces_str` 和 `spaces_num`;相反,我们可以复用 `spaces` 这个更简单的名字。然而,如果尝试使用`mut`,如下所示:
|
||||
|
||||
```rust,ignore
|
||||
let mut spaces = " ";
|
||||
spaces = spaces.len();
|
||||
```
|
||||
|
||||
会导致一个编译时错误,因为不允许改变一个变量的类型:
|
||||
会导致一个编译错误,因为改变一个变量的类型是不被允许的:
|
||||
|
||||
```
|
||||
error[E0308]: mismatched types
|
||||
|
Loading…
Reference in New Issue
Block a user