@@ -76,7 +76,7 @@ commit 77370c073661548dd56bbcb43cc64713585acbba
让我们通过自己动手的方式一起完成一个项目来快速上手 Rust!本章通过展示如何在真实的项目中运用的方式向你介绍一些常用的 Rust 概念。你将会学到let
、match
、方法、关联函数、使用外部 crate 等更多的知识!接下来的章节会探索这些概念的细节。在这一章,我们练习基础。
我们会实现一个经典新手编程问题:猜猜看游戏。它是这么工作的:程序将会随机生成一个 1 到 100 之间的随机整数。接着它会提示玩家输入一个猜测。当输入了一个猜测后,它会告诉提示猜测是太大了还是太小了。猜对了,它会打印出祝贺并退出。
准备一个新项目
-
要创建一个新项目,进入你在第一章创建的项目目录,并使用 Cargo 创建它,像这样:
+
要创建一个新项目,进入你在第一章创建的项目目录,并使用 Cargo 创建它,像这样:
$ cargo new guessing_game --bin
$ cd guessing_game
@@ -146,7 +146,7 @@ println!("Please input your guess.");
接下来,创建一个地方储存用户输入,像这样:
let mut guess = String::new();
-
现在程序开始变得有意思了!这一小行代码发生了很多事。注意这是一个let
语句,用来创建 变量。这里是另外一个例子:
+
现在程序开始变得有意思了!这一小行代码发生了很多事。注意这是一个let
语句,用来创建变量。这里是另外一个例子:
let foo = bar;
这行代码会创建一个叫做foo
的新变量并把它绑定到值bar
上。在 Rust 中,变量默认是不可变的。下面的例子展示了如何在变量名前使用mut
来使一个变量可变:
@@ -157,7 +157,7 @@ let mut bar = 5; // mutable
注意://
开始一个注释,它持续到本行的结尾。Rust 忽略注释中的所有内容。
现在我们知道了let mut guess
会引入一个叫做guess
的可变变量。等号(=
)的另一边是guess
所绑定的值,它是String::new
的结果,这个函数会返回一个String
的新实例。String
是一个标准库提供的字符串类型,它是可增长的、UTF-8 编码的文本块。
-
::new
那一行的::
语法表明new
是String
类型的一个 关联函数(associated function)。关联函数是针对类型实现的,在这个例子中是String
,而不是String
的某个特定实例。一些语言中把它称为 静态方法(static method)。
+
::new
那一行的::
语法表明new
是String
类型的一个关联函数(associated function)。关联函数是针对类型实现的,在这个例子中是String
,而不是String
的某个特定实例。一些语言中把它称为静态方法(static method)。
new
函数创建了一个新的空的String
,你会在很多类型上发现new
函数,因为这是创建某个类型新值的常用函数名。
总结一下,let mut guess = String::new();
这一行创建了一个可变变量,目前它绑定到一个String
新的、空的实例上。哟!
回忆一下我们在程序的第一行使用use std::io;
从标准库中引用输入/输出功能。现在在io
上调用一个关联函数,stdin
:
@@ -167,7 +167,7 @@ let mut bar = 5; // mutable
如果我们在程序的开头没有use std::io
这一行,我们可以把函数调用写成std::io::stdin
这样。stdin
函数返回一个 std::io::Stdin
的实例,这是一个代表终端标准输入句柄的类型。
代码的下一部分,.read_line(&mut guess)
,调用 read_line
方法从标准输入句柄获取用户输入。我们还向read_line()
传递了一个参数:&mut guess
。
read_line
的工作是把获取任何用户键入到标准输入的字符并放入一个字符串中,所以它获取字符串作为一个参数。这个字符串需要是可变的,这样这个方法就可以通过增加用户的输入来改变字符串的内容。
-
&
表明这个参数是一个 引用(reference),它提供了一个允许多个不同部分的代码访问同一份数据而不需要在内存中多次拷贝的方法。引用是一个复杂的功能,而 Rust 的一大优势就是它是安全而优雅操纵引用。完成这个程序并不需要知道这么多细节:第四章会更全面的解释引用。现在,我们只需知道它像变量一样,默认是不可变的。因此,需要写成&mut guess
而不是&guess
来使其可变。
+
&
表明这个参数是一个引用(reference),它提供了一个允许多个不同部分的代码访问同一份数据而不需要在内存中多次拷贝的方法。引用是一个复杂的功能,而 Rust 的一大优势就是它是安全而优雅操纵引用。完成这个程序并不需要知道这么多细节:第四章会更全面的解释引用。现在,我们只需知道它像变量一样,默认是不可变的。因此,需要写成&mut guess
而不是&guess
来使其可变。
这行代码还没有分析完。虽然这是单独一行代码,但它只是一个逻辑上代码行(虽然换行了但仍是一个语句)的第一部分。第二部分是这个方法:
.expect("Failed to read line");
@@ -177,7 +177,7 @@ let mut bar = 5; // mutable
不过,过长的代码行难以阅读,所以最好拆开来写,两行代码两个方法调用。现在来看看这行代码干了什么。
使用Result
类型来处理潜在的错误
之前提到过,read_line
将用户输入放入到传递给它字符串中,不过它也返回一个值————一个io::Result
。Rust 标准库中有很多叫做Result
的类型。一个Result
泛型以及对应子模块的特定版本,比如io::Result
。
-
Result
类型是 枚举(enumerations),通常也写作 enums。枚举拥有固定值集合的类型,而这些值被称为枚举的 成员(variants)。第六章会更详细的介绍枚举。
+
Result
类型是 枚举(enumerations),通常也写作 enums。枚举拥有固定值集合的类型,而这些值被称为枚举的成员(variants)。第六章会更详细的介绍枚举。
对于Result
,它的成员是Ok
或Err
,Ok
表明操作成功了,同时Ok
成员之中包含成功生成的值。Err
意味着操作失败,Err
之中包含操作是为什么或如何失败的信息。
Result
类型的作用是编码错误处理信息。Result
类型的值,正如其他任何类型,拥有定义于其上的方法。io::Result
的实例拥有expect
方法可供调用。如果io::Result
实例的值是Err
,expect
会导致程序崩溃并显示显示你作为参数传递给expect
的信息。如果io::Result
实例的值是Ok
,expect
会获取Ok
中的值并原原本本的返回给你,这样就可以使用它了。在本例中,返回值是用户输入到标准输入的一些字符。
如果不使用expect
,程序也能编译,不过会出现一个警告:
@@ -214,7 +214,7 @@ You guessed: 6
生成一个秘密数字
接下来,需要生成一个秘密数字,用户会尝试猜测它。秘密数字应该每次都不同,这样多玩几次才会有意思。生成一个 1 到 100 之间的随机数这样游戏也不会太难。Rust 标准库中还未包含随机数功能。然而,Rust 团队确实提供了一个rand
crate。
使用 crate 来增加更多功能
-
记住 crate 是一个 Rust 代码的包。我们正在构建的项目是一个 二进制 crate,它生成一个可执行文件。 rand
crate 是一个 库 crate,它包含意在被其他程序使用的代码。
+
记住 crate 是一个 Rust 代码的包。我们正在构建的项目是一个二进制 crate,它生成一个可执行文件。 rand
crate 是一个 库 crate,它包含意在被其他程序使用的代码。
Cargo 对外部 crate 的运用是其真正闪光的地方。在我们可以使用rand
编写代码之前,需要编辑 Cargo.toml 来包含rand
作为一个依赖。现在打开这个文件并在[dependencies]
部分标题(Cargo 为你创建了它)的下面添加如下代码:
Filename: Cargo.toml
[dependencies]
@@ -249,7 +249,7 @@ as a dependency
Cargo 有一个机制来确保每次任何人重新构建代码都会生成相同的成品:Cargo 只会使用你指定的依赖的版本,除非你又手动指定了别的。例如,如果下周rand
crate 的v0.3.15
版本出来了,而它包含一个重要的 bug 修改并也含有一个会破坏代码运行的缺陷的时候会发生什么呢?
这个问题的答案是 Cargo.lock 文件,它在第一次运行cargo build
时被创建并位于 guessing_game 目录。当第一次构建项目时,Cargo 计算出所有符合要求的依赖版本并接着写入 Cargo.lock 文件中。当将来构建项目时,Cargo 发现 Cargo.lock 存在就会使用这里指定的版本,而不是重新进行所有版本的计算。这使得你拥有了一个自动的可重现的构建。换句话说,项目会继续使用0.3.14
直到你显式升级,多亏了 Cargo.lock 文件。我们将会在这个文件编写全部的代码。
更新 crate 到一个新版本
-当你确实需要升级 crate 时,Cargo 提供了另一个命令,update
,他会:
+当你确实需要升级 crate 时,Cargo 提供了另一个命令,update
,他会:
- 忽略 Cargo.lock 文件并计算出所有符合 Cargo.toml 中规格的最新版本。
- 如果成功了,Cargo 会把这些版本写入 Cargo.lock 文件。
@@ -268,7 +268,7 @@ rand = "0.4.0"
下一次运行cargo build
时,Cargo 会更新 registry 中可用的 crate 并根据你指定新版本重新计算rand
的要求。
第十四章会讲到Cargo 和它的生态系统的更多内容,不过目前你只需要了解这么多。Cargo 使得复用库文件变得非常容易,所以 Rustacean 们能够通过组合很多包来编写出更轻巧的项目。
生成一个随机数
-让我们开始使用rand
。下一步是更新 src/main.rs,如列表 2-3:
+让我们开始使用rand
。下一步是更新 src/main.rs,如列表 2-3: