check ch02-00

This commit is contained in:
KaiserY 2017-08-11 18:29:22 +08:00
parent 569dd3f6b3
commit fb9e935e98
3 changed files with 129 additions and 95 deletions

View File

@ -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,37 +73,37 @@ 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⊙)
该行以分号结尾(`;`)。`;` 代表一个表达式的结束和下一个表达式的开始。大部分 Rust 代码行以 `;` 结尾。 该行以分号结尾(`;`)。`;` 代表一个表达式的结束和下一个表达式的开始。大部分 Rust 代码行以 `;` 结尾。
### 编译和运行是两个步骤 ### 编译和运行是两个步骤
“编写并运行 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,39 +116,39 @@ 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` 文件,他们需要先分别安装 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 开始的话,这将会变得简单许多。
由于绝大部分 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 创建项目
让我们使用 Cargo 来创建一个新项目,然后看看与上面的`hello_world`项目有什么不同。回到 projects 目录(或者任何你放置代码的目录): 让我们使用 Cargo 来创建一个新项目,然后看看与上面的 `hello_world` 项目有什么不同。回到 projects 目录(或者任何你放置代码的目录):
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

View File

@ -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]
@ -34,9 +34,9 @@ authors = ["Your Name <you@example.com>"]
如果 Cargo 从环境中获取的开发者信息不正确,修改这个文件并再次保存。 如果 Cargo 从环境中获取的开发者信息不正确,修改这个文件并再次保存。
正如第一章那样,`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,9 +80,9 @@ 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`
```rust,ignore ```rust,ignore
use std::io; use std::io;
@ -101,7 +100,7 @@ fn main() {
`fn` 语法声明了一个新函数,`()` 表明没有参数,`{` 作为函数体的开始。 `fn` 语法声明了一个新函数,`()` 表明没有参数,`{` 作为函数体的开始。
第一章也提及`println!` 是一个在屏幕上打印字符串的宏: 第一章也提及`println!` 是一个在屏幕上打印字符串的宏:
```rust,ignore ```rust,ignore
println!("Guess the number!"); println!("Guess the number!");
@ -109,49 +108,49 @@ 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 编码的可增长文本块。
[string]: https://doc.rust-lang.org/std/string/struct.String.html [string]: https://doc.rust-lang.org/std/string/struct.String.html
`::new` 那一行的 `::` 语法表明 `new``String` 类型的一个 **关联函数***associated function*)。关联函数是针对类型实现的,在这个例子中是 `String`,而不是 `String` 的某个特定实例。一些语言中把它称为**静态方法***static method*)。 `::new` 那一行的 `::` 语法表明 `new``String` 类型的一个 **关联函数***associated function*)。关联函数是针对类型实现的,在这个例子中是 `String`,而不是 `String` 的某个特定实例。一些语言中把它称为 **静态方法***static method*)。
`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");
``` ```
@ -184,19 +183,19 @@ io::stdin().read_line(&mut guess).expect("Failed to read line");
[ioresult]: https://doc.rust-lang.org/std/io/type.Result.html [ioresult]: https://doc.rust-lang.org/std/io/type.Result.html
[result]: https://doc.rust-lang.org/std/result/enum.Result.html [result]: https://doc.rust-lang.org/std/result/enum.Result.html
`Result` 类型是 [*枚举**enumerations*][enums]<!-- ignore -->,通常也写作 *enums*。枚举类型持有固定集合的值,这些值被称为枚举的**成员***variants*)。第六章将介绍枚举的更多细节。 `Result` 类型是 [*枚举**enumerations*][enums]<!-- ignore -->,通常也写作 *enums*。枚举类型持有固定集合的值,这些值被称为枚举的 **成员***variants*)。第六章将介绍枚举的更多细节。
[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!` 占位符打印值

View File

@ -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>