Merge pull request #126 from W4anD0eR96/patch-ch01-ch03

Modify bit and pieces between ch01 and ch03
pull/127/head
KaiserY 7 years ago committed by GitHub
commit 3d77fd0349
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -11,7 +11,7 @@ registry site[crates.io]!我们由衷期待**你**使用 Rust 进行创
[crates.io]: https://crates.io/ [crates.io]: https://crates.io/
本书的目标读者至少应了解一门其它编程语言。阅读本书之后,你应该能自如的编写 Rust 程序。我们将通过短小精干、前后呼应的例子来学习 Rust并展示其多样功能的使用方法同时了解们幕后如何运行。 本书的目标读者至少应了解一门其它编程语言。阅读本书之后,你应该能自如的编写 Rust 程序。我们将通过短小精干、前后呼应的例子来学习 Rust并展示其多样功能的使用方法同时了解们幕后如何运行。
## 为本书做出贡献 ## 为本书做出贡献

@ -6,7 +6,7 @@
使用 Rust 的第一步是安装。你需要网络连接来执行本章的命令,因为将要从网上下载 Rust。 使用 Rust 的第一步是安装。你需要网络连接来执行本章的命令,因为将要从网上下载 Rust。
这里将会展示很多使用终端的命令,这些命令均以 `$` 开头。不需要真的输入`$`,在这里们代表每行命令的起始。网上有很多教程和例子遵循这种惯例:`$` 代表以常规用户身份运行命令,`#` 代表需要用管理员身份运行命令。没有以 `$`(或 `#`)起始的行通常是之前命令的输出。 这里将会展示很多使用终端的命令,这些命令均以 `$` 开头。不需要真的输入`$`,在这里们代表每行命令的起始。网上有很多教程和例子遵循这种惯例:`$` 代表以常规用户身份运行命令,`#` 代表需要用管理员身份运行命令。没有以 `$`(或 `#`)起始的行通常是之前命令的输出。
### 在 Linux 或 Mac 上安装 ### 在 Linux 或 Mac 上安装

@ -73,7 +73,7 @@ fn main() {
} }
``` ```
这几行定义了一个 Rust **函数**。`main` 函数是特殊的:它是每个可执行的 Rust 程序首先执行的。第一行代码表示 “我声明了一个叫做 `main` 的函数,它没有参数也没有返回值。” 如果有参数的话,们的名称应该出现在括号中,`(`和`)`之间。 这几行定义了一个 Rust **函数**。`main` 函数是特殊的:它是每个可执行的 Rust 程序首先执行的。第一行代码表示 “我声明了一个叫做 `main` 的函数,它没有参数也没有返回值。” 如果有参数的话,们的名称应该出现在括号中,`(`和`)`之间。
还须注意函数体被包裹在花括号中,`{`和`}` 之间。Rust 要求所有函数体都要用花括号包裹起来(译者注:有些语言,当函数体只有一行时可以省略花括号,但在 Rust 中是不行的)。一般来说,将左花括号与函数声明置于同一行并以空格分隔,是良好的代码风格。 还须注意函数体被包裹在花括号中,`{`和`}` 之间。Rust 要求所有函数体都要用花括号包裹起来(译者注:有些语言,当函数体只有一行时可以省略花括号,但在 Rust 中是不行的)。一般来说,将左花括号与函数声明置于同一行并以空格分隔,是良好的代码风格。
@ -124,13 +124,13 @@ $ ./main # or .\main.exe on Windows
如果 *main.rs* 是上文所述的 “Hello, world!” 程序,它将会在终端上打印 `Hello, world!` 如果 *main.rs* 是上文所述的 “Hello, world!” 程序,它将会在终端上打印 `Hello, world!`
来自 Ruby、Python 或 JavaScript 这样的动态类型语言背景的同学可能不太习惯将编译和执行分为两个单独的步骤。Rust 是一种 **预编译静态类型语言***ahead-of-time compiled language*),这意味着你可以编译程序并将其交与他人,们不需要安装 Rust 即可运行。相反如果你给他们一个 `.rb`、`.py` 或 `.js` 文件,他们需要先分别安装 RubyPythonJavaScript 实现运行时环境VM不过你只需要一句命令就可以编译和执行程序。这一切都是语言设计上的权衡取舍。 来自 Ruby、Python 或 JavaScript 这样的动态类型语言背景的同学可能不太习惯将编译和执行分为两个单独的步骤。Rust 是一种 **预编译静态类型语言***ahead-of-time compiled language*),这意味着你可以编译程序并将其交与他人,们不需要安装 Rust 即可运行。相反如果你给他们一个 `.rb`、`.py` 或 `.js` 文件,他们需要先分别安装 RubyPythonJavaScript 实现运行时环境VM不过你只需要一句命令就可以编译和执行程序。这一切都是语言设计上的权衡取舍。
使用 `rustc` 编译简单程序是没问题的,不过随着项目的增长,你可能需要控制你项目的方方面面,并且更容易地将代码分享给其它人或项目。接下来,我们要介绍一个叫做 Cargo 的工具,它会帮助你编写真实世界中的 Rust 程序。 使用 `rustc` 编译简单程序是没问题的,不过随着项目的增长,你可能需要控制你项目的方方面面,并且更容易地将代码分享给其它人或项目。接下来,我们要介绍一个叫做 Cargo 的工具,它会帮助你编写真实世界中的 Rust 程序。
## Hello, Cargo! ## Hello, Cargo!
Cargo 是 Rust 的构建系统和包管理工具,同时 Rustacean 们使用 Cargo 来管理他们的 Rust 项目它使得很多任务变得更轻松。例如Cargo 负责构建代码、下载依赖库并编译们。我们把代码需要的库叫做 **依赖***dependencies*)。 Cargo 是 Rust 的构建系统和包管理工具,同时 Rustacean 们使用 Cargo 来管理他们的 Rust 项目它使得很多任务变得更轻松。例如Cargo 负责构建代码、下载依赖库并编译们。我们把代码需要的库叫做 **依赖***dependencies*)。
最简单的 Rust 程序,比如我们刚刚编写的,并没有任何依赖,所以我们只使用了 Cargo 构建代码的功能。随着编写的程序更加复杂,你会想要添加依赖,如果你使用 Cargo 开始的话,这将会变得简单许多。 最简单的 Rust 程序,比如我们刚刚编写的,并没有任何依赖,所以我们只使用了 Cargo 构建代码的功能。随着编写的程序更加复杂,你会想要添加依赖,如果你使用 Cargo 开始的话,这将会变得简单许多。
@ -267,7 +267,7 @@ Cargo 的另一个优点是,不管你使用什么操作系统其命令都是
### 发布release构建 ### 发布release构建
当项目最终准备好发布了,可以使用 `cargo build --release` 来优化编译项目。这会在 *target/release* 而不是 *target/debug* 下生成可执行文件。这些优化可以让 Rust 代码运行的更快,不过启用这些优化也需要消耗更长的编译时间。这也就是为什么会有两种不同的配置:一种为了开发,你需要经常快速重新构建;另一种为了构建给用户最终程序,们不会重新构建,并且希望程序运行得越快越好。如果你在测试代码的运行时间,请确保运行 `cargo build --release` 并使用 *target/release* 下的可执行文件进行测试。 当项目最终准备好发布了,可以使用 `cargo build --release` 来优化编译项目。这会在 *target/release* 而不是 *target/debug* 下生成可执行文件。这些优化可以让 Rust 代码运行的更快,不过启用这些优化也需要消耗更长的编译时间。这也就是为什么会有两种不同的配置:一种为了开发,你需要经常快速重新构建;另一种为了构建给用户最终程序,们不会重新构建,并且希望程序运行得越快越好。如果你在测试代码的运行时间,请确保运行 `cargo build --release` 并使用 *target/release* 下的可执行文件进行测试。
### 把 Cargo 当作习惯 ### 把 Cargo 当作习惯

@ -4,7 +4,7 @@
> <br> > <br>
> commit 2e269ff82193fd65df8a87c06561d74b51ac02f7 > commit 2e269ff82193fd65df8a87c06561d74b51ac02f7
让我们一起动手完成一个项目,来快速上手 Rust本章将介绍 Rust 中常用的一些概念,并通过真实的程序来展示如何运用们。你将会学到更多诸如 `let`、`match`、方法、关联函数、外部 crate 等很多的知识!后继章节会深入探索这些概念的细节。在这一章,我们将练习基础。 让我们一起动手完成一个项目,来快速上手 Rust本章将介绍 Rust 中常用的一些概念,并通过真实的程序来展示如何运用们。你将会学到更多诸如 `let`、`match`、方法、关联函数、外部 crate 等很多的知识!后继章节会深入探索这些概念的细节。在这一章,我们将练习基础。
我们会实现一个经典的新手编程问题:猜猜看游戏。它是这么工作的:程序将会随机生成一个 1 到 100 之间的随机整数。接着它会请玩家猜一个数并输入,然后提示猜测是大了还是小了。如果猜对了,它会打印祝贺信息并退出。 我们会实现一个经典的新手编程问题:猜猜看游戏。它是这么工作的:程序将会随机生成一个 1 到 100 之间的随机整数。接着它会请玩家猜一个数并输入,然后提示猜测是大了还是小了。如果猜对了,它会打印祝贺信息并退出。
@ -396,11 +396,11 @@ Please input your guess.
You guessed: 5 You guessed: 5
``` ```
你应该能得到不同的随机数,同时们应该都是在 1 和 100 之间的。干得漂亮! 你应该能得到不同的随机数,同时们应该都是在 1 和 100 之间的。干得漂亮!
## 比较猜测与秘密数字 ## 比较猜测与秘密数字
现在有了用户输入和一个随机数,我们可以比较们。这个步骤如示例 2-4 所示: 现在有了用户输入和一个随机数,我们可以比较们。这个步骤如示例 2-4 所示:
<span class="filename">文件名: src/main.rs</span> <span class="filename">文件名: src/main.rs</span>
@ -762,4 +762,4 @@ fn main() {
此时此刻,你顺利完成了猜猜看游戏!恭喜! 此时此刻,你顺利完成了猜猜看游戏!恭喜!
这是一个通过动手实践学习 Rust 新概念的项目:`let`、`match`、方法、关联函数、使用外部 crate 等等,接下来的几章,我们将会继续深入。第三章涉及到大部分编程语言都有的概念,比如变量、数据类型和函数,以及如何在 Rust 中使用们。第四章探索所有权ownership这是一个 Rust 同其他语言大不相同的功能。第五章讨论结构体和方法的语法,而第六章侧重解释枚举。 这是一个通过动手实践学习 Rust 新概念的项目:`let`、`match`、方法、关联函数、使用外部 crate 等等,接下来的几章,我们将会继续深入。第三章涉及到大部分编程语言都有的概念,比如变量、数据类型和函数,以及如何在 Rust 中使用们。第四章探索所有权ownership这是一个 Rust 同其他语言大不相同的功能。第五章讨论结构体和方法的语法,而第六章侧重解释枚举。

@ -4,10 +4,10 @@
> <br> > <br>
> commit 04aa3a45eb72855b34213703718f50a12a3eeec8 > commit 04aa3a45eb72855b34213703718f50a12a3eeec8
本章涉及一些几乎所有编程语言都有的概念,以及们在 Rust 中是如何工作的。很多编程语言的核心概念都是共通的,本章中展示的概念都不是 Rust 特有的,不过我们会在 Rust 环境中讨论他们,解释他们的使用习惯。 本章涉及一些几乎所有编程语言都有的概念,以及们在 Rust 中是如何工作的。很多编程语言的核心概念都是共通的,本章中展示的概念都不是 Rust 特有的,不过我们会在 Rust 环境中讨论它们,解释它们的使用习惯。
具体,我们将会学习变量,基本类型,函数,注释和控制流。这些基础知识将会出现在每一个 Rust 程序中,提早学习这些概念会使你拥有坚实的起步基础。 具体,我们将会学习变量,基本类型,函数,注释和控制流。这些基础知识将会出现在每一个 Rust 程序中,提早学习这些概念会为你奠定坚实的起步基础。
> ### 关键字 > ### 关键字
> >
> Rust 语言有一系列保留的 **关键字***keywords*),只能由语言本身使用,像大部分语言一样。你不能使用这些关键字作为变量或函数的名称大部分关键字有特殊的意义,并被用来完成 Rust 程序中的各种任务;一些关键字目前没有相应的功能,是为将来可能添加的功能保留的。可以在附录 A 中找到关键字的列表。 > Rust 语言有一系列保留的 **关键字***keywords*就像大部分语言一样,它们只能由语言本身使用,你不能使用这些关键字作为变量或函数的名称大部分关键字有特殊的意义,并被用来完成 Rust 程序中的各种任务;一些关键字目前没有相应的功能,是为将来可能添加的功能保留的。可以在附录 A 中找到关键字的列表。

@ -4,7 +4,7 @@
> <br> > <br>
> commit d06a6a181fd61704cbf7feb55bc61d518c6469f9 > commit d06a6a181fd61704cbf7feb55bc61d518c6469f9
第二章中提到过,变量默认是 **不可变***immutable*)的。这是利用 Rust 安全和简单并发的优势编写代码一大助力。不过,变量仍然有可变的选项。让我们探讨一下 Rust 拥抱不可变性的原因及方法,以及何时你不想使用不可变性。 第二章中提到过,变量默认是 **不可变***immutable*)的。这是利用 Rust 安全和简单并发的优势编写代码一大助力。不过,你仍然可以使用可变变量。让我们探讨一下 Rust 拥抱不可变性的原因及方法,以及何时你不想使用不可变性。
当变量不可变时,意味着一旦值被绑定上一个名称,你就不能改变这个值。作为说明,通过 `cargo new --bin variables`*projects* 目录生成一个叫做 *variables* 的新项目。 当变量不可变时,意味着一旦值被绑定上一个名称,你就不能改变这个值。作为说明,通过 `cargo new --bin variables`*projects* 目录生成一个叫做 *variables* 的新项目。
@ -34,13 +34,15 @@ error[E0384]: re-assignment of immutable variable `x`
| ^^^^^ re-assignment of immutable variable | ^^^^^ re-assignment of immutable variable
``` ```
这个例子展示了编译器如何帮助你找出程序中的错误。虽然编译错误令人沮丧,那也不过是说程序不能安全的完成你想让它完成的工作;而 **不能** 说明你是不是一个好程序员!有经验的 Rustacean 们一样会遇到编译错误。这些错误给出的原因是 `对不可变变量重新赋值``re-assignment of immutable variable`),因为我们尝试对不可变变量 `x` 赋第二个值。 这个例子展示了编译器如何帮助你找出程序中的错误。虽然编译错误令人沮丧,那也不过是说程序不能安全的完成你想让它完成的工作;而 **不能** 说明你是不是一个好程序员!有经验的 Rustacean 们一样会遇到编译错误。
尝试去改变预设为不可变的值,产生编译错误是很重要的,因为这种情况可能导致 bug如果代码的一部分假设一个值永远也不会改变而另一部分代码改变了它第一部分代码就有可能以不可预料的方式运行。不得不承认这种 bug 难以跟踪,尤其是第二部分代码只是 **有时** 改变其值。 这些错误给出的原因是 `对不可变变量重新赋值``re-assignment of immutable variable`),因为我们尝试对不可变变量 `x` 赋第二个值。
在尝试改变预设为不可变的值的时候产生编译错误是很重要的,因为这种情况可能导致 bug如果代码的一部分假设一个值永远也不会改变而另一部分代码改变了它第一部分代码就有可能以不可预料的方式运行。不得不承认这种 bug 难以跟踪,尤其是第二部分代码只是 **有时** 改变其值。
Rust 编译器保证,如果声明一个值不会变,它就真的不会变。这意味着当阅读和编写代码时,不需要记住如何以及哪里可能会被改变,从而使得代码易于推导。 Rust 编译器保证,如果声明一个值不会变,它就真的不会变。这意味着当阅读和编写代码时,不需要记住如何以及哪里可能会被改变,从而使得代码易于推导。
不过可变性也是非常有用的。变量只是默认不可变可以通过在变量名之前加 `mut` 来使其可变。除了使值可以改变之外,它向读者表明了其他代码将会改变这个变量的意图。 不过可变性也是非常有用的。变量只是默认不可变可以通过在变量名之前加 `mut` 来使其可变。除了使值可以改变之外,它向读者表明了其他代码将会改变这个变量的意图。
例如,改变 *src/main.rs* 并替换其代码为如下: 例如,改变 *src/main.rs* 并替换其代码为如下:
@ -65,9 +67,9 @@ The value of x is: 5
The value of x is: 6 The value of x is: 6
``` ```
通过 `mut`,允许把绑定到 `x` 的值从 `5` 改成 `6`。在一些情况下,你会想要一个变量可变,因为相对只有不可变的风格更容易编写。 通过 `mut`,允许把绑定到 `x` 的值从 `5` 改成 `6`。在一些情况下,你会想用可变变量,因为这样的代码比起只用不可变变量的更容易编写。
除了避免 bug 外,还有多处需要权衡取舍。例如,使用大型数据结构时,适当地使变量可变,可能比复制和返回新分配的实例更快。对于较小的数据结构,总是创建新实例,采用更偏向函数式的风格编程,可能会使代码更易理解,为可读性而遭受性能惩罚或许值得。 除了避免 bug 外,还有很多地方需要权衡取舍。例如,使用大型数据结构时,适当地使用可变变量,可能比复制和返回新分配的实例更快。对于较小的数据结构,总是创建新实例,采用更偏向函数式的风格编程,可能会使代码更易理解,为可读性而遭受性能惩罚或许值得。
### 变量和常量的区别 ### 变量和常量的区别
@ -87,7 +89,7 @@ The value of x is: 6
const MAX_POINTS: u32 = 100_000; const MAX_POINTS: u32 = 100_000;
``` ```
常量在整个程序生命周期中都有效,位于它声明的作用域之中。这使得常量可以作为多处代码使用的全局范围的值,例如一个游戏中所有玩家可以获取的最高分或者光速。 在声明它的作用域之中,常量在整个程序生命周期中都有效,这使得常量可以作为多处代码使用的全局范围的值,例如一个游戏中所有玩家可以获取的最高分或者光速。
将用于整个程序的硬编码的值声明为常量对后来的维护者了解值的意义很有帮助。它也能将硬编码的值汇总一处,为将来可能的修改提供方便。 将用于整个程序的硬编码的值声明为常量对后来的维护者了解值的意义很有帮助。它也能将硬编码的值汇总一处,为将来可能的修改提供方便。
@ -147,4 +149,4 @@ error[E0308]: mismatched types
found type `usize` found type `usize`
``` ```
现在我们探索了变量如何工作,让我们看看更多的数据类型。 现在我们已经了解了变量如何工作,让我们再看看更多的数据类型。

@ -4,9 +4,9 @@
> <br> > <br>
> commit f4bce88a0f4c09aaf0c996021729c6d42907bc2a > commit f4bce88a0f4c09aaf0c996021729c6d42907bc2a
在 Rust 中,任何值都属于一种明确的 **类型***type*),这告诉了 Rust 它被指定何种数据以便明确其处理方式。我们将分两部分探讨一些内建类型标量scalar和复合compound 在 Rust 中,任何值都属于一种明确的 **类型***type*),这告诉了 Rust 它被指定何种数据以便明确其处理方式。我们将分两部分探讨一些内建类型标量scalar和复合compound
Rust 是 **静态类型***statically typed*)语言,也就是说在编译时就必须知道所有变量的类型,这一认知将贯穿整个章节,请在头脑中明确。通过值的形式及其使用方式,编译器通常可以推断出我们想要用的类型。多种类型均有可能时,比如第二章中使用 `parse``String` 转换为数字时,必须增加类型注解,像这样: Rust 是 **静态类型***statically typed*)语言,也就是说在编译时就必须知道所有变量的类型,这一点将贯穿整个章节。通过值的形式及其使用方式,编译器通常可以推断出我们想要用的类型。多种类型均有可能时,比如第二章中使用 `parse``String` 转换为数字时,必须增加类型注解,像这样:
```rust ```rust
let guess: u32 = "42".parse().expect("Not a number!"); let guess: u32 = "42".parse().expect("Not a number!");
@ -28,7 +28,7 @@ error[E0282]: unable to infer enough type information about `_`
### 标量类型 ### 标量类型
**标量***scalar*类型代表一个单独的值。Rust 有四种基本的标量类型:整型、浮点型、布尔类型和字符类型。你可能在其他语言中见过他们,不过让我们深入了解他们在 Rust 中时如何工作的。 **标量***scalar*类型代表一个单独的值。Rust 有四种基本的标量类型:整型、浮点型、布尔类型和字符类型。你可能在其他语言中见过它们,不过让我们深入了解它们在 Rust 中时如何工作的。
#### 整型 #### 整型
@ -48,9 +48,9 @@ error[E0282]: unable to infer enough type information about `_`
每一个有符号的变体可以储存包含从 -(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。 每一个有符号的变体可以储存包含从 -(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` 类型依赖运行程序的计算机架构64 位架构上他们是 64 位的, 32 位架构上他们是 32 位的。 另外,`isize` 和 `usize` 类型依赖运行程序的计算机架构64 位架构上它们是 64 位的, 32 位架构上它们是 32 位的。
可以使用表格 3-2 中的任何一种形式编写数字字面值。注意除字节以外的其它字面值允许使用类型后缀,例如 `57u8`,同时也允许使用 `_` 做为分隔符以方便读数,例如`1_000`。 可以使用表格 3-2 中的任何一种形式编写数字字面值。注意除 byte 以外的其它字面值允许使用类型后缀,例如 `57u8`,同时也允许使用 `_` 做为分隔符以方便读数,例如`1_000`。
<span class="caption">表格 3-2: Rust 中的整型字面值</span> <span class="caption">表格 3-2: Rust 中的整型字面值</span>
@ -84,7 +84,7 @@ fn main() {
#### 数字运算符 #### 数字运算符
Rust 支持所有数字类型常见的基本数学运算操作:加法、减法、乘法、除法以及余数。如下代码展示了如何使用一个 `let` 语句来使用他们: Rust 支持所有数字类型常见的基本数学运算操作:加法、减法、乘法、除法以及取余。下面的代码展示了如何在一个 `let` 语句中使用它们:
<span class="filename">文件名: src/main.rs</span> <span class="filename">文件名: src/main.rs</span>
@ -107,7 +107,7 @@ fn main() {
} }
``` ```
这些语句中的每个表达式使用了一个数学运算符并计算出了一个值,们绑定到了一个变量。附录 B 包含了一个 Rust 提供的所有运算符的列表。 这些语句中的每个表达式使用了一个数学运算符并计算出了一个值,们绑定到了一个变量。附录 B 包含了一个 Rust 提供的所有运算符的列表。
#### 布尔型 #### 布尔型
@ -123,7 +123,7 @@ fn main() {
} }
``` ```
使用布尔值的主要场景是条件表达式,例如 `if` 表达式。在 “控制流”“Control Flow”部分将讲到`if`表达式在 Rust 中如何工作。 使用布尔值的主要场景是条件表达式,例如 `if` 表达式。在 “控制流”“Control Flow”部分将讲到 `if` 表达式在 Rust 中如何工作。
#### 字符类型 #### 字符类型
@ -139,7 +139,7 @@ fn main() {
} }
``` ```
Rust 的 `char` 类型代表了一个 Unicode 标量值Unicode Scalar Value这意味着它可以比 ASCII 表示更多内容。拼音字母Accented letters中文/日文/汉语等象形文字emoji絵文字以及零长度的空白字符对于 Rust `char`类型都是有效的。Unicode 标量值包含从 `U+0000``U+D7FF``U+E000``U+10FFFF` 之间的值。不过,“字符” 并不是一个 Unicode 中的概念,所以人直觉上的 “字符” 可能与 Rust 中的 `char` 并不符合。第八章的 “字符串” 部分将详细讨论这个主题。 Rust 的 `char` 类型代表了一个 Unicode 标量值Unicode Scalar Value这意味着它可以比 ASCII 表示更多内容。拼音字母Accented letters中文/日文/韩文等象形文字emoji絵文字以及零长度的空白字符对于 Rust `char` 类型都是有效的。Unicode 标量值包含从 `U+0000``U+D7FF``U+E000``U+10FFFF` 之间的值。不过,“字符” 并不是一个 Unicode 中的概念,所以人直觉上的 “字符” 可能与 Rust 中的 `char` 并不符合。第八章的 “字符串” 部分将详细讨论这个主题。
### 复合类型 ### 复合类型
@ -173,9 +173,9 @@ fn main() {
} }
``` ```
程序首先创建了一个元组并绑定到 `tup` 变量上。接着使用了 `let` 和一个模式将 `tup` 分成了三个不同的变量,`x`、`y` 和 `z`。这叫做 *解构**destructuring*),因为它将一个元组拆成了三个部分。最后,程序打印出了 `y` 的值,也就是 `6.4` 程序首先创建了一个元组并绑定到 `tup` 变量上。接着使用了 `let` 和一个模式将 `tup` 分成了三个不同的变量,`x`、`y` 和 `z`。这叫做 **解构***destructuring*),因为它将一个元组拆成了三个部分。最后,程序打印出了 `y` 的值,也就是 `6.4`
除了使用模式匹配解构之外,也可以使用点号(`.`)后跟值的索引来直接访问们。例如: 除了使用模式匹配解构之外,也可以使用点号(`.`)后跟值的索引来直接访问们。例如:
<span class="filename">文件名: src/main.rs</span> <span class="filename">文件名: src/main.rs</span>
@ -195,7 +195,7 @@ fn main() {
#### 数组 #### 数组
另一个获取一个多个值集合的方式是 **数组***array*。与元组不同数组中的每个元素的类型必须相同。Rust 中的数组与一些其他语言中的数组不同,因为 Rust 中的数组是固定长度的:一旦声明,们的长度不能增长或缩小。 另一个获取一个多个值集合的方式是 **数组***array*。与元组不同数组中的每个元素的类型必须相同。Rust 中的数组与一些其他语言中的数组不同,因为 Rust 中的数组是固定长度的:一旦声明,们的长度不能增长或缩小。
Rust 中数组的值位于中括号中的逗号分隔的列表中: Rust 中数组的值位于中括号中的逗号分隔的列表中:
@ -207,9 +207,9 @@ fn main() {
} }
``` ```
数组需要在栈stack而不是在堆heap上为数据分配空间(第四章将讨论栈与堆的更多内容),或者是想要确保总是有固定数量的元素时十分有用。虽然它并不如 vector 类型那么灵活。vector 类型是标准库提供的一个 **允许** 增长和缩小长度的类似数组的集合类型。当不确定是应该使用数组还是 vector 的时候,你可能应该使用 vector第八章会详细讨论 vector。 当你想要在栈stack而不是在堆heap上为数据分配空间第四章将讨论栈与堆的更多内容或者是想要确保总是有固定数量的元素时,数组非常有用,虽然它并不如 vector 类型那么灵活。vector 类型是标准库提供的一个 **允许** 增长和缩小长度的类似数组的集合类型。当不确定是应该使用数组还是 vector 的时候,你可能应该使用 vector第八章会详细讨论 vector。
一个你可能想要使用数组而不是 vector 的例子是当程序需要知道一年中月份的名字时。程序不大可能会去增加或减少月份,这时你可以使用数组因为我们知道它总是含有 12 个元素: 一个你可能想要使用数组而不是 vector 的例子是,当程序需要知道一年中月份的名字时,程序不大可能会去增加或减少月份。这时你可以使用数组,因为我们知道它总是含有 12 个元素:
```rust ```rust
let months = ["January", "February", "March", "April", "May", "June", "July", let months = ["January", "February", "March", "April", "May", "June", "July",
@ -235,7 +235,7 @@ fn main() {
##### 无效的数组元素访问 ##### 无效的数组元素访问
如果我们访问数组结尾之后的元素会发生什么呢?比如我们将上面的例子改为如下 如果我们访问数组结尾之后的元素会发生什么呢?比如我们将上面的例子改成下面这样
<span class="filename">文件名: src/main.rs</span> <span class="filename">文件名: src/main.rs</span>

@ -24,7 +24,7 @@ fn another_function() {
Rust 中的函数定义以 `fn` 开始并在函数名后跟一对括号。大括号告诉编译器哪里是函数体的开始和结尾。 Rust 中的函数定义以 `fn` 开始并在函数名后跟一对括号。大括号告诉编译器哪里是函数体的开始和结尾。
可以使用定义过的函数名后跟括号来调用任意函数。因为 `another_function` 已经在程序中定义过了,它可以在 `main` 函数中被调用。注意,源码中 `another_function``main` 函数 **之后** 被定义也可以在其之前定义。Rust 不关心函数定义于何处,只要们被定义了。 可以使用定义过的函数名后跟括号来调用任意函数。因为 `another_function` 已经在程序中定义过了,它可以在 `main` 函数中被调用。注意,源码中 `another_function``main` 函数 **之后** 被定义也可以在其之前定义。Rust 不关心函数定义于何处,只要们被定义了。
让我们开始一个叫做 *functions* 的新二进制项目来进一步探索函数。将上面的 `another_function` 例子写入 *src/main.rs* 中并运行。你应该会看到如下输出: 让我们开始一个叫做 *functions* 的新二进制项目来进一步探索函数。将上面的 `another_function` 例子写入 *src/main.rs* 中并运行。你应该会看到如下输出:
@ -36,11 +36,11 @@ Hello, world!
Another function. Another function.
``` ```
代码在 `main` 函数中按照们出现的顺序被执行。首先,打印 “Hello, world!” 信息,接着 `another_function` 被调用并打印它的信息。 代码在 `main` 函数中按照们出现的顺序被执行。首先,打印 “Hello, world!” 信息,接着 `another_function` 被调用并打印它的信息。
### 函数参数 ### 函数参数
函数也可以被定义为拥有 **参数***parameters*们是作为函数签名一部分的特殊变量。当函数拥有参数时,可以为这些参数提供具体的值。技术上讲,这些具体值被称为参数( *arguments*),不过通常的习惯是倾向于在函数定义中的变量和调用函数时传递的具体值都可以用 “parameter” 和 “argument” 而不加区别。 函数也可以被定义为拥有 **参数***parameters*们是作为函数签名一部分的特殊变量。当函数拥有参数时,可以为这些参数提供具体的值。技术上讲,这些具体值被称为参数(*arguments*),不过通常的习惯是倾向于在函数定义中的变量和调用函数时传递的具体值都可以用 “parameter” 和 “argument” 而不加区别。
如下被重写的 `another_function` 版本展示了 Rust 中参数是什么样的: 如下被重写的 `another_function` 版本展示了 Rust 中参数是什么样的:
@ -67,9 +67,9 @@ The value of x is: 5
`another_function` 的声明有一个叫做 `x` 的参数。`x` 的类型被指定为 `i32`。当 `5` 被传递给 `another_function` 时,`println!` 宏将 `5` 放入格式化字符串中大括号的位置。 `another_function` 的声明有一个叫做 `x` 的参数。`x` 的类型被指定为 `i32`。当 `5` 被传递给 `another_function` 时,`println!` 宏将 `5` 放入格式化字符串中大括号的位置。
在函数签名中**必须** 声明每个参数的类型。这是 Rust 设计中一个经过慎重考虑的决定:要求在函数定义中提供类型注解意味着编译器再也不需要在别的地方要求你注明类型就能知道你的意图。 在函数签名中 **必须** 声明每个参数的类型。这是 Rust 设计中一个经过慎重考虑的决定:要求在函数定义中提供类型注解意味着编译器不需要在别的地方要求你注明类型就能知道你的意图。
当一个函数有多个参数时,使用逗号隔开们,像这样: 当一个函数有多个参数时,使用逗号隔开们,像这样:
<span class="filename">文件名: src/main.rs</span> <span class="filename">文件名: src/main.rs</span>
@ -84,9 +84,9 @@ fn another_function(x: i32, y: i32) {
} }
``` ```
这个例子创建了一个有两个参数的函数,都是 `i32` 类型的。函数打印出了这两个参数的值。注意函数参数并不一定都是相同类型的,这个例子中们只是碰巧相同罢了。 这个例子创建了一个有两个参数的函数,都是 `i32` 类型的。函数打印出了这两个参数的值。注意函数参数并不一定都是相同类型的,这个例子中们只是碰巧相同罢了。
尝试运行代码。使用上面的例子替换当前 *function* 项目的 *src/main.rs* 文件,并 `cargo run` 运行它: 尝试运行代码。使用上面的例子替换当前 *function* 项目的 *src/main.rs* 文件,并 `cargo run` 运行它:
```text ```text
$ cargo run $ cargo run
@ -96,11 +96,11 @@ The value of x is: 5
The value of y is: 6 The value of y is: 6
``` ```
因为我们使用 `5` 作为 `x` 的值`6` 作为 `y` 的值来调用函数,这两个字符串和他们的值被相应打印出来。 因为我们使用 `5` 作为 `x` 的值 `6` 作为 `y` 的值来调用函数,这两个字符串和它们的值被相应打印出来。
### 函数体 ### 函数体
函数体由一系列的语句和一个可选的表达式构成。目前为止,我们只涉及到了没有结尾表达式的函数,不过我们见过表达式作为了语句的一部分。因为 Rust 是一个基于表达式expression-based的语言这是一个需要理解的不同于其他语言重要区别。其他语言并没有这样的区别所以让我们看看语句与表达式有什么区别以及们是如何影响函数体的。 函数体由一系列的语句和一个可选的表达式构成。目前为止,我们只涉及到了没有结尾表达式的函数,不过我们见过表达式作为了语句的一部分。因为 Rust 是一个基于表达式expression-based的语言这是一个需要理解的不同于其他语言重要区别。其他语言并没有这样的区别所以让我们看看语句与表达式有什么区别以及们是如何影响函数体的。
### 语句与表达式 ### 语句与表达式
@ -118,7 +118,7 @@ fn main() {
<span class="caption">列表 3-3包含一个语句的 `main` 函数定义</span> <span class="caption">列表 3-3包含一个语句的 `main` 函数定义</span>
函数定义也是语句上面整个例子本身就是一个语句。 函数定义也是语句上面整个例子本身就是一个语句。
语句并不返回值。因此,不能把`let`语句赋值给另一个变量,比如下面的例子尝试做的: 语句并不返回值。因此,不能把`let`语句赋值给另一个变量,比如下面的例子尝试做的:
@ -144,9 +144,9 @@ error: expected expression, found statement (`let`)
= note: variable declaration using `let` is a statement = note: variable declaration using `let` is a statement
``` ```
`let y = 6` 语句并不返回值,所以并没有 `x` 可以绑定的值。这与其他语言不同,例如 C 和 Ruby们的赋值语句返回所赋的值。在这些语言中,可以这么写 `x = y = 6` 这样 `x``y` 的值都是 `6`;这在 Rust 中可不行。 `let y = 6` 语句并不返回值,所以并没有 `x` 可以绑定的值。这与其他语言不同,例如 C 和 Ruby们的赋值语句返回所赋的值。在这些语言中,可以这么写 `x = y = 6` 这样 `x``y` 的值都是 `6`;这在 Rust 中可不行。
表达式计算出一些值,而且们组成了其余大部分你将会编写的 Rust 代码。考虑一个简单的数学运算,比如 `5 + 6`,这是一个表达式并计算出值 `11`。表达式可以是语句的一部分:在列表 3-3 中有这个语句 `let y = 6;``6` 是一个表达式它计算出的值是 `6`。函数调用是一个表达式。宏调用是一个表达式。我们用来创建新作用域的大括号(代码块),`{}`,也是一个表达式,例如: 表达式计算出一些值,而且们组成了其余大部分你将会编写的 Rust 代码。考虑一个简单的数学运算,比如 `5 + 6`,这是一个表达式并计算出值 `11`。表达式可以是语句的一部分:在列表 3-3 中有这个语句 `let y = 6;``6` 是一个表达式它计算出的值是 `6`。函数调用是一个表达式。宏调用是一个表达式。我们用来创建新作用域的大括号(代码块),`{}`,也是一个表达式,例如:
<span class="filename">文件名: src/main.rs</span> <span class="filename">文件名: src/main.rs</span>
@ -172,11 +172,11 @@ fn main() {
} }
``` ```
这个代码块的值是 `4`。这个值作为 `let` 语句的一部分被绑定到 `y` 上。注意结尾没有分号的那一行,与大部分我们见过的代码行不同。表达式并不包含结尾的分号。如果在表达式的结尾加上分号,他就变成了语句,这也就使其不返回一个值。在接下来的探索中记住函数和表达式都返回值就行了。 是一个代码块,它的值是 `4`。这个值作为 `let` 语句的一部分被绑定到 `y` 上。注意结尾没有分号的那一行,与大部分我们见过的代码行不同。表达式并不包含结尾的分号。如果在表达式的结尾加上分号,他就变成了语句,这也就使其不返回一个值。在接下来的探索中记住函数和表达式都返回值就行了。
### 函数的返回值 ### 函数的返回值
可以向调用它的代码返回值。并不对返回值命名,不过会在一个箭头(`->`)后声明它的类型。在 Rust 中,函数的返回值等同于函数体最后一个表达式的值。这是一个有返回值的函数的例子: 函数可以向调用它的代码返回值。我们并不对返回值命名,不过会在一个箭头(`->`)后声明它的类型。在 Rust 中,函数的返回值等同于函数体最后一个表达式的值。这是一个有返回值的函数的例子:
<span class="filename">文件名: src/main.rs</span> <span class="filename">文件名: src/main.rs</span>
@ -260,4 +260,4 @@ help: consider removing this semicolon:
| ^ | ^
``` ```
主要的错误信息“mismatched types,”(类型不匹配),揭示了代码的核心问题。函数`plus_one` 的定义说明它要返回一个 `i32`,不过语句并不返回一个值,这由那个空元组 `()` 表明。因此,这个函数返回了空元组 `()`这与函数定义相矛盾并导致一个错误。在输出中Rust 提供了一个可能会对修正问题有帮助的信息:它建议去掉分号,这会修复这个错误。 主要的错误信息“mismatched types”类型不匹配揭示了代码的核心问题。函数 `plus_one` 的定义说明它要返回一个 `i32`,不过语句并不返回一个值,这由那个空元组 `()` 表明。因此,这个函数返回了空元组 `()`这与函数定义相矛盾并导致一个错误。在输出中Rust 提供了一个可能会对修正问题有帮助的信息:它建议去掉分号,这会修复这个错误。

@ -4,7 +4,7 @@
> <br> > <br>
> commit d06a6a181fd61704cbf7feb55bc61d518c6469f9 > commit d06a6a181fd61704cbf7feb55bc61d518c6469f9
所有编程语言都力求使们的代码易于理解,不过有时需要提供额外的解释。在这种情况下,程序员在源码中留下记录,或者 **注释***comments*),编译器会忽略们不过其他阅读代码的人可能会用得上。 所有编程语言都力求使们的代码易于理解,不过有时需要提供额外的解释。在这种情况下,程序员在源码中留下记录,或者 **注释***comments*),编译器会忽略们不过其他阅读代码的人可能会用得上。
这是一个注释的例子: 这是一个注释的例子:
@ -30,7 +30,7 @@ fn main() {
} }
``` ```
不过你会经常看到们被以这种格式使用,也就是位于它所解释的代码行的上面一行: 不过你会经常看到们被以这种格式使用,也就是位于它所解释的代码行的上面一行:
<span class="filename">文件名: src/main.rs</span> <span class="filename">文件名: src/main.rs</span>
@ -41,4 +41,4 @@ fn main() {
} }
``` ```
这就是注释的全部。并没有什么特别复杂的 Rust 还有另一种注释,称为文档注释,我们将在 14 章讨论它

@ -28,7 +28,7 @@ fn main() {
<!-- NEXT PARAGRAPH WRAPPED WEIRD INTENTIONALLY SEE #199 --> <!-- NEXT PARAGRAPH WRAPPED WEIRD INTENTIONALLY SEE #199 -->
所有 `if` 表达式以 `if` 关键字开头,它后跟一个条件。在这个例子中,条件检查 `number` 是否有一个小于 5 的值。在条件为真时希望执行的代码块位于紧跟条件之后的大括号中。`if` 表达式中与条件关联的代码块有时被叫做 *arms*,就像第二章 “比较猜测与秘密数字” 部分中讨论到的 `match` 表达式中分支一样。也可以包含一个可选的 `else` 表达式,这里我们就这么做了,来提供一个在条件为假时应当执行的代码块。如果不提供 `else` 表达式并且条件为假时,程序会直接忽略 `if` 代码块并继续执行下面的代码。 所有 `if` 表达式以 `if` 关键字开头,它后跟一个条件。在这个例子中,条件检查 `number` 是否有一个小于 5 的值。在条件为真时希望执行的代码块位于紧跟条件之后的大括号中。`if` 表达式中与条件关联的代码块有时被叫做 *arms*,就像第二章 “比较猜测与秘密数字” 部分中讨论到的 `match` 表达式中分支一样。也可以包含一个可选的 `else` 表达式来提供一个在条件为假时应当执行的代码块,这里我们就这么做了。如果不提供 `else` 表达式并且条件为假时,程序会直接忽略 `if` 代码块并继续执行下面的代码。
尝试运行代码,应该能看到如下输出: 尝试运行代码,应该能看到如下输出:
@ -81,7 +81,7 @@ error[E0308]: mismatched types
found type `{integer}` found type `{integer}`
``` ```
这个错误表明 Rust 期望一个 `bool` 不过却得到了一个整型。Rust 并不会尝试自动地将非布尔值转换为布尔值,不像例如 Ruby 和 JavaScript 这样的语言。必须总是显式地使用 `boolean` 作为 `if` 的条件。例如如果想 要`if` 代码块只在一个数字不等于 `0` 时执行,可以把 `if` 表达式修改为如下 这个错误表明 Rust 期望一个 `bool` 不过却得到了一个整型。不像 Ruby 或 JavaScript 这样的语言Rust 并不会尝试自动地将非布尔值转换为布尔值。必须总是显式地使用布尔值作为 `if` 的条件。例如,如果想要 `if` 代码块只在一个数字不等于 `0` 时执行,可以把 `if` 表达式修改成下面这样
<span class="filename">文件名: src/main.rs</span> <span class="filename">文件名: src/main.rs</span>
@ -202,13 +202,13 @@ error[E0308]: if and else have incompatible types
### 使用循环重复执行 ### 使用循环重复执行
多次执行同一段代码是很常用的。为了这个功能Rust 提供了多种 **循环***loops*)。一个循环执行循环体中的代码直到结尾并紧接着回到开头继续执行。为了实验一下循环,让我们创建一个叫做 *loops* 的新项目。 多次执行同一段代码是很常用的Rust 为此提供了多种 **循环***loops*)。一个循环执行循环体中的代码直到结尾并紧接着回到开头继续执行。为了实验一下循环,让我们创建一个叫做 *loops* 的新项目。
Rust 有三种循环类型:`loop`、`while` 和 `for`。让我们每一个都试试。 Rust 有三种循环类型:`loop`、`while` 和 `for`。让我们每一个都试试。
#### 使用 `loop` 重复执行代码 #### 使用 `loop` 重复执行代码
`loop` 关键字告诉 Rust 一遍又一遍执行一段代码直到你明确要求停止。 `loop` 关键字告诉 Rust 一遍又一遍执行一段代码直到你明确要求停止。
作为一个例子,将 *loops* 目录中的 *src/main.rs* 文件修改为如下: 作为一个例子,将 *loops* 目录中的 *src/main.rs* 文件修改为如下:
@ -321,7 +321,7 @@ fn main() {
例如,在示例 3-5 的代码中,如果从数组 `a` 中移除一个元素但忘记更新条件为 `while index < 4`,代码将会 panic。使用`for`循环的话,就不需要惦记着在更新数组元素数量时修改其他的代码了。 例如,在示例 3-5 的代码中,如果从数组 `a` 中移除一个元素但忘记更新条件为 `while index < 4`,代码将会 panic。使用`for`循环的话,就不需要惦记着在更新数组元素数量时修改其他的代码了。
`for` 循环的安全性和简洁性使得它在成为 Rust 中使用最多的循环结构。即使是在想要循环执行代码特定次数时,例如示例 3-5 中使用 `while` 循环的倒计时例子,大部分 Rustacean 也会使用 `for` 循环。这么做的方式是使用 `Range`,它是标准库提供的用来生成从一个数字开始到另一个数字结束的所有数字序列的类型。 `for` 循环的安全性和简洁性使得它在成为 Rust 中使用最多的循环结构。即使是在想要循环执行代码特定次数时,例如示例 3-5 中使用 `while` 循环的倒计时例子,大部分 Rustacean 也会使用 `for` 循环。这么做的方式是使用 `Range`,它是标准库提供的用来生成从一个数字开始到另一个数字之前结束的所有数字序列的类型。
下面是一个使用 `for` 循环来倒计时的例子,它还使用了一个我们还未讲到的方法,`rev`,用来反转 range 下面是一个使用 `for` 循环来倒计时的例子,它还使用了一个我们还未讲到的方法,`rev`,用来反转 range
@ -340,7 +340,7 @@ fn main() {
## 总结 ## 总结
你做到了!这是一个相当可观的章节:你学习了变量,标量和 `if` 表达式,还有循环!如果你想要实践本章讨论的概念,尝试构建如下的程序: 你做到了!这是一个章节:你学习了变量,标量和 `if` 表达式,还有循环!如果你想要实践本章讨论的概念,尝试构建如下的程序:
* 相互转换摄氏与华氏温度 * 相互转换摄氏与华氏温度
* 生成 n 阶斐波那契数列 * 生成 n 阶斐波那契数列

Loading…
Cancel
Save