mirror of
https://github.com/KaiserY/trpl-zh-cn
synced 2024-11-09 08:51:18 +08:00
check ch02-00
This commit is contained in:
parent
569dd3f6b3
commit
fb9e935e98
@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
Linux 和 Mac:
|
Linux 和 Mac:
|
||||||
|
|
||||||
```
|
```text
|
||||||
$ mkdir ~/projects
|
$ mkdir ~/projects
|
||||||
$ cd ~/projects
|
$ cd ~/projects
|
||||||
$ mkdir hello_world
|
$ mkdir hello_world
|
||||||
@ -55,7 +55,7 @@ fn main() {
|
|||||||
|
|
||||||
保存文件,并回到终端窗口。在 Linux 或 OSX 上,输入如下命令:
|
保存文件,并回到终端窗口。在 Linux 或 OSX 上,输入如下命令:
|
||||||
|
|
||||||
```
|
```text
|
||||||
$ rustc main.rs
|
$ rustc main.rs
|
||||||
$ ./main
|
$ ./main
|
||||||
Hello, world!
|
Hello, world!
|
||||||
@ -65,7 +65,7 @@ Hello, world!
|
|||||||
|
|
||||||
### 分析 Rust 程序
|
### 分析 Rust 程序
|
||||||
|
|
||||||
现在,让我们回过头来,仔细看看“Hello, world!”程序到底发生了什么。这是拼图的第一片:
|
现在,让我们回过头来仔细看看 “Hello, world!” 程序到底发生了什么。这是拼图的第一片:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -73,19 +73,19 @@ fn main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
这几行定义了一个 Rust **函数**。一个叫 `main` 的函数,没有参数也没有返回值。如果有参数的话,它们应该出现在括弧中,`(`和`)`之间。`main` 函数是特殊的:它是每一个可执行的 Rust 程序的入口点。
|
这几行定义了一个 Rust **函数**。`main` 函数是特殊的:它是每个可执行的 Rust 程序首先执行的。第一行代码表示 “我声明了一个叫做 `main` 的函数,它没有参数也没有返回值。” 如果有参数的话,他们的名称应该出现在括号中,`(`和`)`之间。
|
||||||
|
|
||||||
还须注意函数体被包裹在花括号中,`{`和`}` 之间。所有函数体都要用花括号包裹起来(译者注:有些语言,当函数体只有一行时可以省略花括号,但 Rust 中是不行的)。一般来说,将左花括号与函数声明置于一行,并以空格分隔,是良好的代码风格。
|
还须注意函数体被包裹在花括号中,`{`和`}` 之间。Rust 要求所有函数体都要用花括号包裹起来(译者注:有些语言,当函数体只有一行时可以省略花括号,但 Rust 中是不行的)。一般来说,将左花括号与函数声明置于同一行并以空格分隔,是良好的代码风格。
|
||||||
|
|
||||||
在 `main()` 函数中:
|
在 `main()` 函数中:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
println!("Hello, world!");
|
println!("Hello, world!");
|
||||||
```
|
```
|
||||||
|
|
||||||
一行代码完成这个小程序的所有工作:在屏幕上打印文本。这里有很多细节需要注意。首先 Rust 使用 4 个空格的缩进风格,而不是 1 个制表符(tab)。
|
这行代码完成这个小程序的所有工作:在屏幕上打印文本。这里有很多细节需要注意。首先 Rust 使用 4 个空格的缩进风格,而不是 1 个制表符(tab)。
|
||||||
|
|
||||||
第二个重要的部分是`println!()`。这是 **宏**,Rust 元编程(metaprogramming)的关键所在。而调用一个函数,则要像这样:`println`(没有`!`)。我们将在 21 章 E 小节中更加详细的讨论宏,现在你只需记住,当看到符号 `!` 的时候,调用的是宏而不是普通函数。
|
第二个重要的部分是 `println!()`。这给称为 Rust **宏**,Rust 元编程(metaprogramming)的关键所在。如果是调用函数,则应看起来像这样:`println`(没有`!`)。我们将在附录 E 中更加详细的讨论宏,现在你只需记住,当看到符号 `!` 的时候,调用的是宏而不是普通函数。
|
||||||
|
|
||||||
接下来,`"Hello, world!"` 是一个 **字符串**。我们把这个字符串作为一个参数传递给 `println!`,它负责在屏幕上打印这个字符串。轻松加愉快!(⊙o⊙)
|
接下来,`"Hello, world!"` 是一个 **字符串**。我们把这个字符串作为一个参数传递给 `println!`,它负责在屏幕上打印这个字符串。轻松加愉快!(⊙o⊙)
|
||||||
|
|
||||||
@ -93,17 +93,17 @@ fn main() {
|
|||||||
|
|
||||||
### 编译和运行是两个步骤
|
### 编译和运行是两个步骤
|
||||||
|
|
||||||
“编写并运行 Rust 程序”部分,展示了如何创建运行程序。现在我们将拆分并检查每一步操作。
|
“编写并运行 Rust 程序” 部分中展示了如何运行新创建的程序。现在我们将拆分并检查每一步操作。
|
||||||
|
|
||||||
运行一个 Rust 程序之前,必须先编译它。可以通过 `rustc` 命令来使用 Rust 编译器,并传递源文件的名字给它,如下:
|
在运行一个 Rust 程序之前必须先进行编译。可以通过 `rustc` 命令来使用 Rust 编译器,并传递源文件的名字给它,如下:
|
||||||
|
|
||||||
```
|
```text
|
||||||
$ rustc main.rs
|
$ rustc main.rs
|
||||||
```
|
```
|
||||||
|
|
||||||
如果你有 C 或 C++ 背景,就会发现这与 `gcc` 和 `clang` 类似。编译成功后,Rust 应该会输出一个二进制可执行文件,在 Linux 或 OSX 上在 shell 中你可以通过`ls`命令看到如下:
|
如果你有 C 或 C++ 背景,就会发现这与 `gcc` 和 `clang` 类似。编译成功后,Rust 应该会输出一个二进制可执行文件,在 Linux 或 OSX 上在 shell 中可以通过 `ls` 命令看到如下内容:
|
||||||
|
|
||||||
```
|
```text
|
||||||
$ ls
|
$ ls
|
||||||
main main.rs
|
main main.rs
|
||||||
```
|
```
|
||||||
@ -116,31 +116,31 @@ main.exe
|
|||||||
main.rs
|
main.rs
|
||||||
```
|
```
|
||||||
|
|
||||||
这表示我们有两个文件:*.rs* 后缀的源文件,和可执行文件(在 Windows下是 *main.exe*,其它平台是 *main*)。然后运行 *main* 或 *main.exe* 文件,像这样:
|
这表示我们有两个文件:*.rs* 后缀的源文件,和可执行文件(在 Windows下是 *main.exe*,其它平台是 *main*)。余下需要做的就是运行 *main* 或 *main.exe* 文件,如下:
|
||||||
|
|
||||||
```
|
```text
|
||||||
$ ./main # or .\main.exe on Windows
|
$ ./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` 文件,他们需要先分别安装 Ruby,Python,JavaScript 实现(运行时环境,VM),不过你只需要一句命令就可以编译和执行程序。这一切都是语言设计上的权衡取舍。
|
来自 Ruby、Python 或 JavaScript 这样的动态类型语言背景的同学,可能不太习惯将编译和执行分为两个单独的步骤。Rust 是一种 **预编译静态类型语言**(*ahead-of-time compiled language*),这意味着你可以编译程序并将其交与他人,他们不需要安装 Rust 即可运行。相反如果你给他们一个 `.rb`、`.py` 或 `.js` 文件,他们需要先分别安装 Ruby,Python,JavaScript 实现(运行时环境,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 开始的话,这将会变得简单许多。
|
||||||
|
|
||||||
由于绝大部分 Rust 项目使用 Cargo,本书接下来的部分将假设你使用它。如果使用之前介绍的官方安装包的话,它自带 Cargo。如果通过其他方式安装的话,可以在终端输入如下命令,检查是否安装了 Cargo:
|
由于绝大部分 Rust 项目使用 Cargo,本书接下来的部分将假设你使用它。如果使用之前介绍的官方安装包的话,则自带了 Cargo。如果通过其他方式安装的话,可以在终端输入如下命令检查是否安装了 Cargo:
|
||||||
|
|
||||||
```
|
```text
|
||||||
$ cargo --version
|
$ cargo --version
|
||||||
```
|
```
|
||||||
|
|
||||||
如果出现了版本号,一切 OK!如果出现类似“`command not found`”的错误,你应该查看安装文档以确定如何单独安装 Cargo。
|
如果出现了版本号,一切 OK!如果出现类似 `command not found` 的错误,你应该查看相应安装文档以确定如何单独安装 Cargo。
|
||||||
|
|
||||||
### 使用 Cargo 创建项目
|
### 使用 Cargo 创建项目
|
||||||
|
|
||||||
@ -148,7 +148,7 @@ $ cargo --version
|
|||||||
|
|
||||||
Linux 和 Mac:
|
Linux 和 Mac:
|
||||||
|
|
||||||
```
|
```text
|
||||||
$ cd ~/projects
|
$ cd ~/projects
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -158,20 +158,20 @@ Windows:
|
|||||||
> cd %USERPROFILE%\projects
|
> cd %USERPROFILE%\projects
|
||||||
```
|
```
|
||||||
|
|
||||||
并在任何操作系统运行:
|
并在任何操作系统下运行:
|
||||||
|
|
||||||
```
|
```text
|
||||||
$ cargo new hello_cargo --bin
|
$ cargo new hello_cargo --bin
|
||||||
$ cd hello_cargo
|
$ cd hello_cargo
|
||||||
```
|
```
|
||||||
|
|
||||||
我们向 `cargo new` 传递了 `--bin`,因为我们的目标是生成一个可执行程序,而不是一个库。可执行程序是二进制可执行文件,通常就叫做 **二进制文件**(*binaries*)。项目的名称被定为 `hello_cargo`,同时 Cargo 在一个同名目录中创建它的文件,接着我们可以进入查看。
|
我们向 `cargo new` 传递了 `--bin`,因为我们的目标是生成一个可执行程序,而不是一个库。可执行程序是二进制可执行文件,通常就叫做 **二进制文件**(*binaries*)。项目的名称被定为 `hello_cargo`,同时 Cargo 在一个同名目录中创建它的文件,接着我们可以进入查看。
|
||||||
|
|
||||||
如果列出 *hello_cargo* 目录中的文件,将会看到 Cargo 生成了一个文件和一个目录:一个 *Cargo.toml* 文件和一个 *src* 目录,*main.rs* 文件位于目录中。它也在 *hello_cargo* 目录初始化了一个 git 仓库,以及一个 *.gitignore* 文件;你可以通过`--vcs`参数,切换到其它版本控制系统(VCS),或者不使用 VCS。
|
如果列出 *hello_cargo* 目录中的文件,将会看到 Cargo 生成了一个文件和一个目录:一个 *Cargo.toml* 文件和一个 *src* 目录,*main.rs* 文件位于 *src* 目录中。它也在 *hello_cargo* 目录初始化了一个 git 仓库,以及一个 *.gitignore* 文件;你可以通过`--vcs`参数切换到其它版本控制系统(VCS),或者不使用 VCS。
|
||||||
|
|
||||||
使用文本编辑器(IDE)打开 *Cargo.toml* 文件。它应该看起来像这样:
|
使用文本编辑器(工具请随意)打开 *Cargo.toml* 文件。它应该看起来像这样:
|
||||||
|
|
||||||
<span class="filename">Filename: Cargo.toml</span>
|
<span class="filename">文件名: Cargo.toml</span>
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[package]
|
[package]
|
||||||
@ -188,11 +188,13 @@ authors = ["Your Name <you@example.com>"]
|
|||||||
|
|
||||||
第一行,`[package]`,是一个段落标题,表明下面的语句用来配置一个包。随着我们在这个文件增加更多的信息,还将增加其他段落。
|
第一行,`[package]`,是一个段落标题,表明下面的语句用来配置一个包。随着我们在这个文件增加更多的信息,还将增加其他段落。
|
||||||
|
|
||||||
最后一行,`[dependencies]`,是项目依赖的 *crates*(Rust 代码包)的段落的开始,这样 Cargo 就知道下载和编译它们了。这个项目并不需要任何其他的 crate,不过在下一章猜猜看教程会需要。
|
接下来的三行设置了三个 Cargo 所需的配置,他们告诉 Cargo 需要编译这个项目:名称、版本和作者。Cargo 从环境中获取你的名称和 email 信息。如果不正确,请修改并保存此文件。
|
||||||
|
|
||||||
|
最后一行,`[dependencies]`,是项目依赖的 *crates* 列表(我们这样称呼 Rust 代码包)段落的开始,这样 Cargo 就知道下载和编译它们了。这个项目并不需要任何其他的 crate,不过在下一章猜猜看教程会用得上。
|
||||||
|
|
||||||
现在看看 *src/main.rs*:
|
现在看看 *src/main.rs*:
|
||||||
|
|
||||||
<span class="filename">Filename: src/main.rs</span>
|
<span class="filename">文件名: src/main.rs</span>
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -200,7 +202,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Cargo 为你生成了一个“Hello World!”,正如我们之前编写的那个!目前为止,之前项目与 Cargo 生成项目区别有:
|
Cargo 为你生成了一个 “Hello World!”,正如我们之前编写的那个!目前为止,之前项目与 Cargo 生成项目的区别有:
|
||||||
|
|
||||||
- 代码位于 *src* 目录
|
- 代码位于 *src* 目录
|
||||||
- 项目根目录包含一个 *Cargo.toml* 配置文件
|
- 项目根目录包含一个 *Cargo.toml* 配置文件
|
||||||
@ -213,14 +215,14 @@ Cargo 期望源文件位于 *src* 目录,将项目根目录留给 README、lic
|
|||||||
|
|
||||||
现在让我们看看通过 Cargo 构建和运行 Hello World 程序有什么不同。为此,输入如下命令:
|
现在让我们看看通过 Cargo 构建和运行 Hello World 程序有什么不同。为此,输入如下命令:
|
||||||
|
|
||||||
```
|
```text
|
||||||
$ cargo build
|
$ cargo build
|
||||||
Compiling hello_cargo v0.1.0 (file:///projects/hello_cargo)
|
Compiling hello_cargo v0.1.0 (file:///projects/hello_cargo)
|
||||||
```
|
```
|
||||||
|
|
||||||
这应该创建 *target/debug/hello_cargo*(或者在 Windows 上是 *target\debug\hello_cargo.exe*)可执行文件,可以通过这个命令运行:
|
这应该会创建 *target/debug/hello_cargo*(或者在 Windows 上是 *target\debug\hello_cargo.exe*)可执行文件,可以通过这个命令运行:
|
||||||
|
|
||||||
```
|
```text
|
||||||
$ ./target/debug/hello_cargo # or .\target\debug\hello_cargo.exe on Windows
|
$ ./target/debug/hello_cargo # or .\target\debug\hello_cargo.exe on Windows
|
||||||
Hello, world!
|
Hello, world!
|
||||||
```
|
```
|
||||||
@ -229,7 +231,7 @@ Hello, world!
|
|||||||
|
|
||||||
首次运行 `cargo build` 的时候,Cargo 会在项目根目录创建一个新文件,*Cargo.lock*,它看起来像这样:
|
首次运行 `cargo build` 的时候,Cargo 会在项目根目录创建一个新文件,*Cargo.lock*,它看起来像这样:
|
||||||
|
|
||||||
<span class="filename">Filename: Cargo.lock</span>
|
<span class="filename">文件名: Cargo.lock</span>
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[root]
|
[root]
|
||||||
@ -237,19 +239,19 @@ name = "hello_cargo"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
```
|
```
|
||||||
|
|
||||||
Cargo 使用 *Cargo.lock* 来记录程序的依赖。这个项目并没有依赖,所以内容比较少。事实上,你自己永远也不需要碰这个文件,让 Cargo 处理它就行了。
|
Cargo 使用 *Cargo.lock* 来记录程序的依赖。这个项目并没有依赖,所以其内容比较少。事实上,你自己永远也不需要碰这个文件,让 Cargo 处理它就行了。
|
||||||
|
|
||||||
我们刚刚使用 `cargo build` 构建了项目并使用 `./target/debug/hello_cargo` 运行了它,也可以使用 `cargo run` 编译并运行:
|
我们刚刚使用 `cargo build` 构建了项目并使用 `./target/debug/hello_cargo` 运行了程序,也可以使用 `cargo run` 同时编译并运行:
|
||||||
|
|
||||||
```
|
```text
|
||||||
$ cargo run
|
$ cargo run
|
||||||
Running `target/debug/hello_cargo`
|
Running `target/debug/hello_cargo`
|
||||||
Hello, world!
|
Hello, world!
|
||||||
```
|
```
|
||||||
|
|
||||||
注意这一次并没有出现“正在编译 `hello_cargo`”的输出。Cargo 发现文件并没有被改变,直接运行了二进制文件。如果修改了源文件的话,将会出现像这样的输出:
|
注意这一次并没有出现 Cargo 正在编译 `hello_cargo` 的输出。Cargo 发现文件并没有被改变,就直接运行了二进制文件。如果修改了源文件的话,Cargo 会在运行之前重新构建项目,并会出现像这样的输出:
|
||||||
|
|
||||||
```
|
```text
|
||||||
$ cargo run
|
$ cargo run
|
||||||
Compiling hello_cargo v0.1.0 (file:///projects/hello_cargo)
|
Compiling hello_cargo v0.1.0 (file:///projects/hello_cargo)
|
||||||
Running `target/debug/hello_cargo`
|
Running `target/debug/hello_cargo`
|
||||||
@ -259,24 +261,24 @@ Hello, world!
|
|||||||
所以现在又出现更多的不同:
|
所以现在又出现更多的不同:
|
||||||
|
|
||||||
- 使用 `cargo build` 构建项目(或使用 `cargo run` 一步构建并运行),而不是使用`rustc`
|
- 使用 `cargo build` 构建项目(或使用 `cargo run` 一步构建并运行),而不是使用`rustc`
|
||||||
- 有别于将构建结果放在源码目录,Cargo 将它放到 *target/debug* 目录。
|
- 有别于将构建结果放在与源码相同的目录,Cargo 会将其放到 *target/debug* 目录。
|
||||||
|
|
||||||
Cargo 的另一个优点是,不管你使用什么操作系统,它的命令都是一样的,所以之后我们将不再为 Linux 和 Mac 以及 Windows 提供相应的命令。
|
Cargo 的另一个优点是,不管你使用什么操作系统其命令都是一样的,所以本书之后将不再为 Linux 和 Mac 以及 Windows 提供相应的命令。
|
||||||
|
|
||||||
### 发布构建
|
### 发布(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 当作习惯
|
||||||
|
|
||||||
对于简单项目, Cargo 并不比 `rustc` 更有价值,不过随着开发的进行终将体现它的价值。对于拥有多个 crate 的复杂项目,让 Cargo 来协调构建将更简单。有了 Cargo,只需运行`cargo build`,然后一切将有序运行。即便这个项目很简单,也它使用了很多你之后的 Rust 生涯将会用得上的实用工具。其实你可以开始任何你想要从事的项目,使用下面的命令:
|
对于简单项目, Cargo 并不比 `rustc` 提供了更多的优势,不过随着开发的深入终将证明其价值。对于拥有多个 crate 的复杂项目,让 Cargo 来协调构建将更简单。有了 Cargo,只需运行`cargo build`,然后一切将有序运行。即便这个项目很简单,它现在也使用了很多你之后的 Rust 生涯将会用得上的实用工具。其实你可以使用下面的命令开始任何你想要从事的项目:
|
||||||
|
|
||||||
```
|
```text
|
||||||
$ git clone someurl.com/someproject
|
$ git clone someurl.com/someproject
|
||||||
$ cd someproject
|
$ cd someproject
|
||||||
$ cargo build
|
$ cargo build
|
||||||
```
|
```
|
||||||
|
|
||||||
> 注意:如果想要了解 Cargo 更多的细节,请阅读官方的 [Cargo guide],它覆盖了所有的功能。
|
> 注意:如果想要了解 Cargo 更多的细节,请阅读官方的 [Cargo guide],它覆盖了 Cargo 所有的功能。
|
||||||
|
|
||||||
[Cargo guide]: http://doc.crates.io/guide.html
|
[Cargo guide]: http://doc.crates.io/guide.html
|
||||||
|
@ -1,27 +1,27 @@
|
|||||||
# 猜猜看
|
# 猜猜看
|
||||||
|
|
||||||
> [ch02-00-guessing-game-tutorial.md](https://github.com/rust-lang/book/blob/master/src/ch02-00-guessing-game-tutorial.md)
|
> [ch02-00-guessing-game-tutorial.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch02-00-guessing-game-tutorial.md)
|
||||||
> <br>
|
> <br>
|
||||||
> commit e6d6caab41471f7115a621029bd428a812c5260e
|
> commit 2e269ff82193fd65df8a87c06561d74b51ac02f7
|
||||||
|
|
||||||
让我们亲自动手,快速熟悉 Rust!本章将介绍 Rust 中常用的一些概念,并通过真实的程序来展示如何运用。你将会学到更多诸如 `let`、`match`、方法、关联函数、外部 crate 等知识!后继章节会深入探索这些概念的细节。在这一章,我们将练习基础。
|
让我们一起动手完成一个项目,来快速上手 RUst!本章将介绍 Rust 中常用的一些概念,并通过真实的程序来展示如何运用他们。你将会学到更多诸如 `let`、`match`、方法、关联函数、外部 crate 等很多的知识!后继章节会深入探索这些概念的细节。在这一章,我们将练习基础。
|
||||||
|
|
||||||
我们会实现一个经典的新手编程问题:猜猜看游戏。它是这么工作的:程序将会随机生成一个 1 到 100 之间的随机整数。接着它会请玩家猜一个数并输入,然后提示猜测是大了还是小了。如果猜对了,它会在退出前祝贺你。
|
我们会实现一个经典的新手编程问题:猜猜看游戏。它是这么工作的:程序将会随机生成一个 1 到 100 之间的随机整数。接着它会请玩家猜一个数并输入,然后提示猜测是大了还是小了。如果猜对了,它会打印祝贺信息并退出。
|
||||||
|
|
||||||
## 准备一个新项目
|
## 准备一个新项目
|
||||||
|
|
||||||
要创建一个新项目,进入第一章创建的**项目**目录,使用 Cargo 创建它:
|
要创建一个新项目,进入第一章中创建的 *projects* 目录,使用 Cargo 新建一个项目,如下:
|
||||||
|
|
||||||
```
|
```text
|
||||||
$ cargo new guessing_game --bin
|
$ cargo new guessing_game --bin
|
||||||
$ cd guessing_game
|
$ cd guessing_game
|
||||||
```
|
```
|
||||||
|
|
||||||
第一个命令,`cargo new`,获取项目的名称(`guessing_game`)作为第一个参数。`--bin`参数告诉 Cargo 创建一个二进制项目,与第一章类似。第二个命令进入到新创建的项目目录。
|
第一个命令,`cargo new`,它获取项目的名称(`guessing_game`)作为第一个参数。`--bin` 参数告诉 Cargo 创建一个二进制项目,与第一章类似。第二个命令进入到新创建的项目目录。
|
||||||
|
|
||||||
看看生成的 *Cargo.toml* 文件:
|
看看生成的 *Cargo.toml* 文件:
|
||||||
|
|
||||||
<span class="filename">Filename: Cargo.toml</span>
|
<span class="filename">文件名: Cargo.toml</span>
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[package]
|
[package]
|
||||||
@ -36,7 +36,7 @@ authors = ["Your Name <you@example.com>"]
|
|||||||
|
|
||||||
正如第一章那样,`cargo new` 生成了一个 “Hello, world!” 程序。查看 *src/main.rs* 文件:
|
正如第一章那样,`cargo new` 生成了一个 “Hello, world!” 程序。查看 *src/main.rs* 文件:
|
||||||
|
|
||||||
<span class="filename">Filename: src/main.rs</span>
|
<span class="filename">文件名: src/main.rs</span>
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -44,25 +44,24 @@ fn main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
现在让我们使用 `cargo run`,编译运行一步到位:
|
现在编译 “Hello, world!” 程序,使用 `cargo run` 编译运行一步到位:
|
||||||
|
|
||||||
|
```text
|
||||||
```sh
|
|
||||||
$ cargo run
|
$ cargo run
|
||||||
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
|
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
|
||||||
Running `target/debug/guessing_game`
|
Running `target/debug/guessing_game`
|
||||||
Hello, world!
|
Hello, world!
|
||||||
```
|
```
|
||||||
|
|
||||||
`run` 命令适合用在需要快速迭代的项目,而这个游戏就是:我们需要在下一步迭代之前快速测试。
|
`run` 命令适合用于需要快速迭代的项目,而这个游戏便是这样的项目:我们需要在下一步迭代之前快速测试。
|
||||||
|
|
||||||
重新打开 *src/main.rs* 文件。我们将会在这个文件中编写全部代码。
|
重新打开 *src/main.rs* 文件。我们将会在这个文件中编写全部代码。
|
||||||
|
|
||||||
## 处理一次猜测
|
## 处理一次猜测
|
||||||
|
|
||||||
程序的第一部分请求和处理用户输入,并检查输入是否符合预期。首先,需要有一个让玩家输入猜测的地方。在 *src/main.rs* 中输入列表 2-1 中的代码。
|
程序的第一部分请求和处理用户输入,并检查输入是否符合预期的格式。首先,允许用户输入猜测。在 *src/main.rs* 中输入列表 2-1 中的代码。
|
||||||
|
|
||||||
<span class="filename">Filename: src/main.rs</span>
|
<span class="filename">文件名: src/main.rs</span>
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
use std::io;
|
use std::io;
|
||||||
@ -81,7 +80,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
<span class="caption">Listing 2-1: Code to get a guess from the user and print it out</span>
|
<span class="caption">列表 2-1:获取用户猜测并打印的代码</span>
|
||||||
|
|
||||||
这些代码包含很多信息,我们一点一点地过一遍。为了获取用户输入并打印结果作为输出,我们需要将 `io`(输入/输出)库引入当前作用域。`io` 库来自于标准库(也被称为`std`):
|
这些代码包含很多信息,我们一点一点地过一遍。为了获取用户输入并打印结果作为输出,我们需要将 `io`(输入/输出)库引入当前作用域。`io` 库来自于标准库(也被称为`std`):
|
||||||
|
|
||||||
@ -101,7 +100,7 @@ fn main() {
|
|||||||
|
|
||||||
`fn` 语法声明了一个新函数,`()` 表明没有参数,`{` 作为函数体的开始。
|
`fn` 语法声明了一个新函数,`()` 表明没有参数,`{` 作为函数体的开始。
|
||||||
|
|
||||||
第一章也提及,`println!` 是一个在屏幕上打印字符串的宏:
|
第一章也提及了 `println!` 是一个在屏幕上打印字符串的宏:
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
println!("Guess the number!");
|
println!("Guess the number!");
|
||||||
@ -109,30 +108,30 @@ println!("Guess the number!");
|
|||||||
println!("Please input your guess.");
|
println!("Please input your guess.");
|
||||||
```
|
```
|
||||||
|
|
||||||
这些代码仅仅打印提示,介绍游戏的内容然后请用户输入。
|
这些代码仅仅打印提示,介绍游戏的内容然后请求用户输入。
|
||||||
|
|
||||||
### 用变量储存值
|
### 使用变量储存值
|
||||||
|
|
||||||
接下来,创建一个地方储存用户输入,像这样:
|
接下来,创建一个地方储存用户输入,像这样:
|
||||||
|
|
||||||
```rust
|
```rust,ignore
|
||||||
let mut guess = String::new();
|
let mut guess = String::new();
|
||||||
```
|
```
|
||||||
|
|
||||||
现在程序开始变得有意思了!这一小行代码发生了很多事。注意这是一个 `let` 语句,用来创建 **变量**。这里是另外一个例子:
|
现在程序开始变得有意思了!这一小行代码发生了很多事。注意这是一个 `let` 语句,用来创建 **变量**。这里是另外一个例子:
|
||||||
|
|
||||||
```rust
|
```rust,ignore
|
||||||
let foo = bar;
|
let foo = bar;
|
||||||
```
|
```
|
||||||
|
|
||||||
这行代码会创建一个叫做 `foo` 的新变量并把它绑定到值 `bar` 上。在 Rust 中,变量默认是不可变的。下面的例子展示了如何在变量名前使用 `mut` 来使一个变量可变:
|
这行代码新建了一个叫做 `foo` 的变量并把它绑定到值 `bar` 上。在 Rust 中,变量默认是不可变的。下面的例子展示了如何在变量名前使用 `mut` 来使一个变量可变:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let foo = 5; // immutable
|
let foo = 5; // immutable
|
||||||
let mut bar = 5; // mutable
|
let mut bar = 5; // mutable
|
||||||
```
|
```
|
||||||
|
|
||||||
> 注意:`//` 开始一个注释,它持续到本行的结尾。Rust 忽略注释中的所有内容。
|
> 注意:`//` 语法开始一个持续到本行的结尾的注释。Rust 忽略注释中的所有内容。
|
||||||
|
|
||||||
现在我们知道了 `let mut guess` 会引入一个叫做 `guess` 的可变变量。等号(`=`)的右边是 `guess` 所绑定的值,它是 `String::new` 的结果,这个函数会返回一个 `String` 的新实例。[`String`][string]<!-- ignore --> 是一个标准库提供的字符串类型,它是 UTF-8 编码的可增长文本块。
|
现在我们知道了 `let mut guess` 会引入一个叫做 `guess` 的可变变量。等号(`=`)的右边是 `guess` 所绑定的值,它是 `String::new` 的结果,这个函数会返回一个 `String` 的新实例。[`String`][string]<!-- ignore --> 是一个标准库提供的字符串类型,它是 UTF-8 编码的可增长文本块。
|
||||||
|
|
||||||
@ -142,16 +141,16 @@ let mut bar = 5; // mutable
|
|||||||
|
|
||||||
`new` 函数创建了一个新的空 `String`,你会在很多类型上发现 `new` 函数,这是创建类型实例的惯用函数名。
|
`new` 函数创建了一个新的空 `String`,你会在很多类型上发现 `new` 函数,这是创建类型实例的惯用函数名。
|
||||||
|
|
||||||
总结一下,`let mut guess = String::new();` 这一行创建了一个可变变量,绑定到一个新的 `String` 空实例上。
|
总结一下,`let mut guess = String::new();` 这一行创建了一个可变变量,当前它绑定到一个新的 `String` 空实例上。
|
||||||
|
|
||||||
回忆一下,我们在程序的第一行使用 `use std::io;` 从标准库中引入“输入输出”。现在调用 `io` 的关联函数 `stdin`:
|
回忆一下,我们在程序的第一行使用 `use std::io;` 从标准库中引入了输入/输出功能。现在调用 `io` 的关联函数 `stdin`:
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
io::stdin().read_line(&mut guess)
|
io::stdin().read_line(&mut guess)
|
||||||
.expect("Failed to read line");
|
.expect("Failed to read line");
|
||||||
```
|
```
|
||||||
|
|
||||||
如果程序的开头没有 `use std::io` 这一行,我们可以把函数调用写成 `std::io::stdin`。`stdin` 函数返回一个 [`std::io::Stdin`][iostdin]<!-- ignore --> 的实例,这代表终端标准输入句柄的类型。
|
如果程序的开头没有 `use std::io` 这一行,可以把函数调用写成 `std::io::stdin`。`stdin` 函数返回一个 [`std::io::Stdin`][iostdin]<!-- ignore --> 的实例,这代表终端标准输入句柄的类型。
|
||||||
|
|
||||||
[iostdin]: https://doc.rust-lang.org/std/io/struct.Stdin.html
|
[iostdin]: https://doc.rust-lang.org/std/io/struct.Stdin.html
|
||||||
|
|
||||||
@ -159,19 +158,19 @@ io::stdin().read_line(&mut guess)
|
|||||||
|
|
||||||
[read_line]: https://doc.rust-lang.org/std/io/struct.Stdin.html#method.read_line
|
[read_line]: https://doc.rust-lang.org/std/io/struct.Stdin.html#method.read_line
|
||||||
|
|
||||||
`read_line` 的工作是,无论用户在标准输入中键入什么内容,都将其存入一个字符串中,因此它需要字符串作为参数。这个字符串参数应该是可变的,以便 `read_line` 将用户输入附加上去。
|
`read_line` 的工作是,无论用户在标准输入中键入什么内容,都将其存入一个字符串中,因此它需要字符串作为参数。这个字符串参数应该是可变的,以便 `read_line` 将用户输入附加上去。
|
||||||
|
|
||||||
`&` 表示这个参数是一个 **引用**(*reference*),它允许多处代码访问同一处数据,而无需在内存中多次拷贝。引用是一个复杂的特性,Rust 的一个主要优势就是安全而简单的操纵引用。完成当前程序并不需要了解如此多细节:第四章会更全面的解释引用。现在,我们只需知道它像变量一样,默认是不可变的,需要写成 `&mut guess` 而不是 `&guess` 来使其可变。
|
`&` 表示这个参数是一个 **引用**(*reference*),它允许多处代码访问同一处数据,而无需在内存中多次拷贝。引用是一个复杂的特性,Rust 的一个主要优势就是安全而简单的操纵引用。完成当前程序并不需要了解如此多细节:第四章会更全面的解释引用。现在,我们只需知道它像变量一样,默认是不可变的,需要写成 `&mut guess` 而不是 `&guess` 来使其可变。
|
||||||
|
|
||||||
我们还没有分析完这行代码。虽然这是单独一行代码,但它是一个逻辑行(虽然换行了但仍是一个语句)的第一部分。第二部分是这个方法:
|
我们还没有分析完这行代码。虽然这是单独一行代码,但它是一个逻辑行(虽然换行了但仍是一个语句)的第一部分。第二部分是这个方法:
|
||||||
|
|
||||||
```rust
|
```rust,ignore
|
||||||
.expect("Failed to read line");
|
.expect("Failed to read line");
|
||||||
```
|
```
|
||||||
|
|
||||||
当使用 `.expect()` 语法调用方法时,通过‘换行并缩进’来把长行拆开,是明智的。我们完全可以这样写:
|
当使用 `.foo()` 语法调用方法时,通过换行并缩进来把长行拆开,是明智的。我们完全可以这样写:
|
||||||
|
|
||||||
```rust
|
```rust,ignore
|
||||||
io::stdin().read_line(&mut guess).expect("Failed to read line");
|
io::stdin().read_line(&mut guess).expect("Failed to read line");
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -188,15 +187,15 @@ io::stdin().read_line(&mut guess).expect("Failed to read line");
|
|||||||
|
|
||||||
[enums]: ch06-00-enums.html
|
[enums]: ch06-00-enums.html
|
||||||
|
|
||||||
对于 `Result`,它的成员是 `Ok` 或 `Err`,`Ok` 表示操作成功,内部包含产生的值。`Err` 意味着操作失败,包含失败的前因后果。
|
对于 `Result`,它的成员是 `Ok` 或 `Err`,`Ok` 表示操作成功,内部包含成功时产生的值。`Err` 意味着操作失败,包含失败的前因后果。
|
||||||
|
|
||||||
`Result` 类型的作用是编码错误处理信息。`Result` 类型的值,像其他类型一样,拥有定义于其上的方法。`io::Result` 的实例拥有[`expect`方法][expect]<!-- ignore -->,如果实例的值是 `Err`,`expect` 会导致程序崩溃,并显示当做参数传递给 `expect` 的信息;如果实例的值是 `Ok`,`expect` 会获取 `Ok` 中的值并原样返回。在本例中,这个值是用户输入到标准输入中的字节的数量。
|
`Result` 类型的作用是编码错误处理信息。`Result` 类型的值,像其他类型一样,拥有定义于其上的方法。`io::Result` 的实例拥有[`expect` 方法][expect]<!-- ignore -->,如果实例的值是 `Err`,`expect` 会导致程序崩溃,并显示当做参数传递给 `expect` 的信息。如果 `read_line` 方法返回 `Err`,则可能是来源于底层操作系统错误的结果。如果 `io::Result` 实例的值是 `Ok`,`expect` 会获取 `Ok` 中的值并原样返回。在本例中,这个值是用户输入到标准输入中的字节的数量。
|
||||||
|
|
||||||
[expect]: https://doc.rust-lang.org/std/result/enum.Result.html#method.expect
|
[expect]: https://doc.rust-lang.org/std/result/enum.Result.html#method.expect
|
||||||
|
|
||||||
如果不使用 `expect`,程序也能编译,不过会出现一个警告:
|
如果不使用 `expect`,程序也能编译,不过会出现一个警告:
|
||||||
|
|
||||||
```
|
```text
|
||||||
$ cargo build
|
$ cargo build
|
||||||
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
|
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
|
||||||
src/main.rs:10:5: 10:39 warning: unused result which must be used,
|
src/main.rs:10:5: 10:39 warning: unused result which must be used,
|
||||||
@ -205,7 +204,7 @@ src/main.rs:10 io::stdin().read_line(&mut guess);
|
|||||||
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
```
|
```
|
||||||
|
|
||||||
Rust 警告我们没有使用 `read_line` 的返回值 `Result`,说明有一个可能的错误没处理。想消除警告,就老实的写错误处理,不过我们就是希望程序在出现问题时立即崩溃,所以直接使用 `expect`。第九章会学习如何从错误中恢复。
|
Rust 警告我们没有使用 `read_line` 的返回值 `Result`,说明有一个可能的错误没有处理。想消除警告,就老实的写错误处理,不过我们就是希望程序在出现问题时立即崩溃,所以直接使用 `expect`。第九章会学习如何从错误中恢复。
|
||||||
|
|
||||||
### 使用 `println!` 占位符打印值
|
### 使用 `println!` 占位符打印值
|
||||||
|
|
||||||
|
@ -10,7 +10,8 @@
|
|||||||
<base href="{{ path_to_root }}">
|
<base href="{{ path_to_root }}">
|
||||||
|
|
||||||
<link rel="stylesheet" href="book.css">
|
<link rel="stylesheet" href="book.css">
|
||||||
<link href='https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800' rel='stylesheet' type='text/css'>
|
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800" rel="stylesheet" type="text/css">
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Source+Code+Pro:500" rel="stylesheet" type="text/css">
|
||||||
|
|
||||||
<link rel="shortcut icon" href="{{ favicon }}">
|
<link rel="shortcut icon" href="{{ favicon }}">
|
||||||
|
|
||||||
@ -19,6 +20,13 @@
|
|||||||
|
|
||||||
<link rel="stylesheet" href="highlight.css">
|
<link rel="stylesheet" href="highlight.css">
|
||||||
<link rel="stylesheet" href="tomorrow-night.css">
|
<link rel="stylesheet" href="tomorrow-night.css">
|
||||||
|
<link rel="stylesheet" href="ayu-highlight.css">
|
||||||
|
|
||||||
|
<!-- Custom theme -->
|
||||||
|
{{#each additional_css}}
|
||||||
|
<link rel="stylesheet" href="{{this}}">
|
||||||
|
{{/each}}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.page-wrapper.has-warning > .nav-chapters {
|
.page-wrapper.has-warning > .nav-chapters {
|
||||||
/* add height for warning content & margin */
|
/* add height for warning content & margin */
|
||||||
@ -85,8 +93,18 @@
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
{{#if mathjax_support}}
|
||||||
<!-- MathJax -->
|
<!-- MathJax -->
|
||||||
<script type="text/javascript" src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<!-- Fetch Clipboard.js from CDN but have a local fallback -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/clipboard.js/1.6.1/clipboard.min.js"></script>
|
||||||
|
<script>
|
||||||
|
if (typeof Clipboard == 'undefined') {
|
||||||
|
document.write(unescape("%3Cscript src='clipboard.min.js'%3E%3C/script%3E"));
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<!-- Fetch JQuery from CDN but have a local fallback -->
|
<!-- Fetch JQuery from CDN but have a local fallback -->
|
||||||
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
|
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
|
||||||
@ -95,6 +113,14 @@
|
|||||||
document.write(unescape("%3Cscript src='jquery.js'%3E%3C/script%3E"));
|
document.write(unescape("%3Cscript src='jquery.js'%3E%3C/script%3E"));
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- Fetch store.js from local - TODO add CDN when 2.x.x is available on cdnjs -->
|
||||||
|
<script src="store.js"></script>
|
||||||
|
|
||||||
|
<!-- Custom JS script -->
|
||||||
|
{{#each additional_js}}
|
||||||
|
<script type="text/javascript" src="{{this}}"></script>
|
||||||
|
{{/each}}
|
||||||
</head>
|
</head>
|
||||||
<body class="light">
|
<body class="light">
|
||||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||||
@ -177,8 +203,15 @@
|
|||||||
<!-- Livereload script (if served using the cli tool) -->
|
<!-- Livereload script (if served using the cli tool) -->
|
||||||
{{{livereload}}}
|
{{{livereload}}}
|
||||||
|
|
||||||
|
{{#if playpens_editable}}
|
||||||
|
<script src="{{ ace_js }}" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="{{ editor_js }}" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="{{ mode_rust_js }}" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="{{ theme_dawn_js }}" type="text/javascript" charset="utf-8"></script>
|
||||||
|
<script src="{{ theme_tomorrow_night_js }}" type="text/javascript" charset="utf-8"></script>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
<script src="highlight.js"></script>
|
<script src="highlight.js"></script>
|
||||||
<script src="store.js"></script>
|
|
||||||
<script src="book.js"></script>
|
<script src="book.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
Loading…
Reference in New Issue
Block a user