mirror of
https://github.com/KaiserY/trpl-zh-cn
synced 2024-11-09 00:43:59 +08:00
commit
c6db0e3d95
@ -84,7 +84,7 @@ fn main() {
|
||||
|
||||
### 编译和运行是两个步骤
|
||||
|
||||
在“编写并运行 Rust 程序”部分,展示了如何运行一个新创建的程序。现在我们将拆分并检查每一步操作。
|
||||
“编写并运行 Rust 程序”部分,展示了如何创建运行程序。现在我们将拆分并检查每一步操作。
|
||||
|
||||
运行一个 Rust 程序之前,必须先编译它。可以通过 `rustc` 命令来使用 Rust 编译器,并传递源文件的名字给它,如下:
|
||||
|
||||
@ -117,7 +117,7 @@ $ ./main # or .\main.exe on Windows
|
||||
|
||||
来自 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!
|
||||
|
||||
@ -238,7 +238,7 @@ $ cargo run
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
注意这一次,并没有出现告诉我们 Cargo 正在编译 `hello_cargo` 的输出。Cargo 发现文件并没有被改变,直接运行了二进制文件。如果修改了源文件的话,将会出现像这样的输出:
|
||||
注意这一次并没有出现“正在编译 `hello_cargo`”的输出。Cargo 发现文件并没有被改变,直接运行了二进制文件。如果修改了源文件的话,将会出现像这样的输出:
|
||||
|
||||
```
|
||||
$ cargo run
|
||||
@ -256,11 +256,11 @@ Cargo 的另一个优点是,不管你使用什么操作系统,它的命令
|
||||
|
||||
### 发布构建
|
||||
|
||||
当项目最终准备好发布了,可以使用`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 并不能比`rustc`提供更多的价值,不过随着开发的进行终将体现它的价值。对于拥有多个 crate 的复杂项目,让 Cargo 来协调构建将更简单。有了 Cargo,只需运行`cargo build`,然后一切将有序运行。即便这个项目很简单,也它使用了很多你之后的 Rust 生涯将会用得上的实用工具。其实你可以开始任何你想要从事的项目,使用下面的命令:
|
||||
对于简单项目, Cargo 并不比 `rustc` 更有价值,不过随着开发的进行终将体现它的价值。对于拥有多个 crate 的复杂项目,让 Cargo 来协调构建将更简单。有了 Cargo,只需运行`cargo build`,然后一切将有序运行。即便这个项目很简单,也它使用了很多你之后的 Rust 生涯将会用得上的实用工具。其实你可以开始任何你想要从事的项目,使用下面的命令:
|
||||
|
||||
```
|
||||
$ git clone someurl.com/someproject
|
||||
|
@ -4,13 +4,13 @@
|
||||
> <br>
|
||||
> commit e6d6caab41471f7115a621029bd428a812c5260e
|
||||
|
||||
让我们通过自己动手的方式一起完成一个项目来快速上手 Rust!本章将介绍一些常用的 Rust 概念,并通过真实的程序来展示如何运用他们。你将会学到`let`、`match`、方法、关联函数、使用外部 crate 等更多的知识!接下来的章节会探索这些概念的细节。在这一章,我们将练习基础。
|
||||
让我们亲自动手,快速熟悉 Rust!本章将介绍 Rust 中常用的一些概念,并通过真实的程序来展示如何运用。你将会学到更多诸如 `let`、`match`、方法、关联函数、外部 crate 等知识!后继章节会深入探索这些概念的细节。在这一章,我们将练习基础。
|
||||
|
||||
我们会实现一个经典的新手编程问题:猜猜看游戏。它是这么工作的:程序将会随机生成一个 1 到 100 之间的随机整数。接着它会提示玩家输入一个猜测。当输入了一个猜测后,它会提示猜测是太大了还是太小了。如果猜对了,它会打印出祝贺并退出。
|
||||
我们会实现一个经典的新手编程问题:猜猜看游戏。它是这么工作的:程序将会随机生成一个 1 到 100 之间的随机整数。接着它会请玩家猜一个数并输入,然后提示猜测是大了还是小了。如果猜对了,它会在退出前祝贺你。
|
||||
|
||||
## 准备一个新项目
|
||||
|
||||
要创建一个新项目,进入在第一章创建的**项目**目录,像这样使用 Cargo 创建它:
|
||||
要创建一个新项目,进入第一章创建的**项目**目录,使用 Cargo 创建它:
|
||||
|
||||
```
|
||||
$ cargo new guessing_game --bin
|
||||
@ -60,7 +60,7 @@ Hello, world!
|
||||
|
||||
## 处理一次猜测
|
||||
|
||||
程序的第一部分会请求用户输入,处理输入,并检查输入是否为期望的形式。首先,允许玩家输入一个猜测。在 *src/main.rs* 中输入列表 2-1 中的代码。
|
||||
程序的第一部分会请求用户输入,处理输入,并检查输入是否符合预期。首先,允许玩家输入一个猜测。在 *src/main.rs* 中输入列表 2-1 中的代码。
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -83,17 +83,17 @@ fn main() {
|
||||
|
||||
<span class="caption">Listing 2-1: Code to get a guess from the user and print it out</span>
|
||||
|
||||
这些代码包含很多信息,所以让我们一点一点地过一遍。为了获取用户输入并接着打印结果作为输出,我们需要将`io`(输入/输出)库引入作用域中。`io`库来自于标准库(也被称为`std`):
|
||||
这些代码包含很多信息,我们一点一点地过一遍。为了获取用户输入并打印结果作为输出,我们需要将 `io`(输入/输出)库引入当前作用域。`io`库来自于标准库(也被称为`std`):
|
||||
|
||||
```rust,ignore
|
||||
use std::io;
|
||||
```
|
||||
|
||||
Rust 默认只在每个程序的 [*prelude*][prelude]<!-- ignore --> 中引用很少的一些类型。如果想要使用的类型并不在 prelude 中,你必须使用一个`use`语句显式的将其引入到作用域中。使用`std::io`库将提供很多`io`相关的功能,接受用户输入的功能。
|
||||
Rust 默认只在每个程序的 [*prelude*][prelude]<!-- ignore --> 中引用很少的一些类型。如果想要使用的类型并不在 prelude 中,你必须使用一个`use`语句显式的将其引入到作用域中。`std::io`库提供很多`io`相关的功能,比如接受用户输入。
|
||||
|
||||
[prelude]: https://doc.rust-lang.org/std/prelude/index.html
|
||||
|
||||
正如第一章所讲,`main`函数是程序的入口点:
|
||||
如第一章所提及,`main`函数是程序的入口点:
|
||||
|
||||
```rust,ignore
|
||||
fn main() {
|
||||
@ -101,7 +101,7 @@ fn main() {
|
||||
|
||||
`fn`语法声明了一个新函数,`()`表明没有参数,`{`作为函数体的开始。
|
||||
|
||||
第一章也讲到了,`println!`是一个在屏幕上打印字符串的宏:
|
||||
第一章也提及,`println!`是一个在屏幕上打印字符串的宏:
|
||||
|
||||
```rust,ignore
|
||||
println!("Guess the number!");
|
||||
@ -109,7 +109,7 @@ println!("Guess the number!");
|
||||
println!("Please input your guess.");
|
||||
```
|
||||
|
||||
这些代码仅仅打印一个提示,说明游戏的内容并请求用户输入。
|
||||
这些代码仅仅打印提示,介绍游戏的内容然后请用户输入。
|
||||
|
||||
### 用变量储存值
|
||||
|
||||
@ -125,7 +125,7 @@ let mut guess = String::new();
|
||||
let foo = bar;
|
||||
```
|
||||
|
||||
这行代码会创建一个叫做`foo`的新变量并把它绑定到值`bar`上。在 Rust 中,变量默认是不可变的。下面的例子展示了如何在变量名前使用`mut`来使一个变量可变:
|
||||
这行代码会创建一个叫做 `foo` 的新变量并把它绑定到值 `bar` 上。在 Rust 中,变量默认是不可变的。下面的例子展示了如何在变量名前使用 `mut` 来使一个变量可变:
|
||||
|
||||
```rust
|
||||
let foo = 5; // immutable
|
||||
@ -134,67 +134,67 @@ let mut bar = 5; // mutable
|
||||
|
||||
> 注意:`//` 开始一个注释,它持续到本行的结尾。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
|
||||
|
||||
`::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
|
||||
io::stdin().read_line(&mut guess)
|
||||
.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
|
||||
|
||||
代码的下一部分,`.read_line(&mut guess)`,调用 [`read_line`][read_line]<!-- ignore --> 方法从标准输入句柄获取用户输入。我们还向`read_line()`传递了一个参数:`&mut guess`。
|
||||
代码的下一部分,`.read_line(&mut guess)`,调用 [`read_line`][read_line]<!-- ignore --> 方法从标准输入句柄获取用户输入。我们还向 `read_line()` 传递了一个参数:`&mut guess`。
|
||||
|
||||
[read_line]: https://doc.rust-lang.org/std/io/struct.Stdin.html#method.read_line
|
||||
|
||||
`read_line`的工作是把获取任何用户键入到标准输入的字符并放入一个字符串中,所以它获取字符串作为一个参数。这个字符串需要是可变的,这样这个方法就可以通过增加用户的输入来改变字符串的内容。
|
||||
`read_line` 的工作是,无论用户在标准输入中键入什么内容,都将其存入一个字符串中,因此它需要字符串作为参数。这个字符串参数应该是可变的,以便 `read_line` 将用户输入附加上去。
|
||||
|
||||
`&`表明这个参数是一个**引用**(*reference*),它提供了一个允许多个不同部分的代码访问同一份数据而不需要在内存中多次拷贝的方法。引用是一个复杂的功能,而 Rust 的一大优势就是它是如何安全而优雅操纵引用的。完成这个程序并不需要知道这么多细节:第四章会更全面的解释引用。现在,我们只需知道它像变量一样,默认是不可变的。因此,需要写成`&mut guess`而不是`&guess`来使其可变。
|
||||
`&` 表示这个参数是一个**引用**(*reference*),它允许多处代码访问同一处数据,而无需在内存中多次拷贝。引用是一个复杂的特性,Rust 的一个主要优势就是安全而简单的操纵引用。完成当前程序并不需要了解如此多细节:第四章会更全面的解释引用。现在,我们只需知道它像变量一样,默认是不可变的,需要写成 `&mut guess` 而不是 `&guess` 来使其可变。
|
||||
|
||||
我们还没有分析完这行代码。虽然这是单独一行代码,但它只是一个逻辑上代码行(虽然换行了但仍是一个语句)的第一部分。第二部分是这个方法:
|
||||
我们还没有分析完这行代码。虽然这是单独一行代码,但它是一个逻辑行(虽然换行了但仍是一个语句)的第一部分。第二部分是这个方法:
|
||||
|
||||
```rust,ignore
|
||||
```rust
|
||||
.expect("Failed to read line");
|
||||
```
|
||||
|
||||
当使用`.expect()`语法调用方法时,明智的选择是换行并留出空白(缩进)来把长的代码行拆开。我们可以把代码写成这样:
|
||||
当使用 `.expect()` 语法调用方法时,通过‘换行并缩进’来把长行拆开,是明智的。我们完全可以这样写:
|
||||
|
||||
```rust,ignore
|
||||
io::stdin().read_line(&mut guess).expect("Failed to read line");
|
||||
```
|
||||
|
||||
不过,过长的代码行难以阅读,所以最好拆开来写,两行代码两个方法调用。现在来看看这行代码干了什么。
|
||||
不过,过长的行难以阅读,所以最好拆开来写,两行代码两个方法调用。现在来看看这行代码干了什么。
|
||||
|
||||
### 使用`Result`类型来处理潜在的错误
|
||||
### 使用 `Result` 类型来处理潜在的错误
|
||||
|
||||
之前提到过,`read_line`将用户输入放入到传递给它字符串中,不过它也返回一个值——在这个例子中,一个[`io::Result`][ioresult]<!-- ignore -->。Rust 标准库中有很多叫做`Result`的类型。一个[`Result`][result]<!-- ignore -->泛型以及对应子模块的特定版本,比如`io::Result`。
|
||||
之前提到,`read_line` 将用户输入附加到传递给它字符串中,不过它也返回一个值——在这个例子中是 [`io::Result`][ioresult]<!-- ignore -->。Rust 标准库中有很多叫做 `Result` 的类型。一个 [`Result`][result]<!-- ignore --> 泛型以及对应子模块的特定版本,比如 `io::Result`。
|
||||
|
||||
[ioresult]: https://doc.rust-lang.org/std/io/type.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
|
||||
|
||||
对于`Result`,它的成员是`Ok`或`Err`,`Ok`表明操作成功了,同时`Ok`成员之中包含成功生成的值。`Err`意味着操作失败,`Err`之中包含操作是为什么或如何失败的信息。
|
||||
对于 `Result`,它的成员是 `Ok` 或 `Err`,`Ok` 表示操作成功,内部包含产生的值。`Err` 意味着操作失败,包含失败的前因后果。
|
||||
|
||||
`Result`类型的作用是编码错误处理信息。`Result`类型的值,正如其他任何类型,拥有定义于其上的方法。`io::Result`的实例拥有[`expect`方法][expect]<!-- ignore -->可供调用。如果`io::Result`实例的值是`Err`,`expect`会导致程序崩溃并显示显示你作为参数传递给`expect`的信息。如果`io::Result`实例的值是`Ok`,`expect`会获取`Ok`中的值并原原本本的返回给你。在本例中,这个值是用户输入到标准输入中的字节的数量。
|
||||
`Result` 类型的作用是编码错误处理信息。`Result`类型的值,像其他类型一样,拥有定义于其上的方法。`io::Result` 的实例拥有[`expect`方法][expect]<!-- ignore -->,如果实例的值是 `Err`,`expect` 会导致程序崩溃,并显示当做参数传递给 `expect` 的信息;如果实例的值是 `Ok`,`expect` 会获取 `Ok` 中的值并原样返回。在本例中,这个值是用户输入到标准输入中的字节的数量。
|
||||
|
||||
[expect]: https://doc.rust-lang.org/std/result/enum.Result.html#method.expect
|
||||
|
||||
如果不使用`expect`,程序也能编译,不过会出现一个警告:
|
||||
如果不使用 `expect`,程序也能编译,不过会出现一个警告:
|
||||
|
||||
```
|
||||
$ cargo build
|
||||
@ -205,13 +205,13 @@ src/main.rs:10 io::stdin().read_line(&mut guess);
|
||||
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
```
|
||||
|
||||
Rust 警告我们没有使用`read_line`返回的值`Result`,表明程序没有处理一个可能的错误。消除警告的正确方式是老实编写错误处理,不过因为我们仅仅希望程序出现问题就崩溃,可以直接使用`expect`。第九章会学习从错误中恢复的内容。
|
||||
Rust 警告我们没有使用 `read_line` 的返回值 `Result`,说明有一个可能的错误没处理。想消除警告,就老实的写错误处理,不过我们就是希望程序在出现问题时立即崩溃,所以直接使用 `expect`。第九章会学习如何从错误中恢复。
|
||||
|
||||
### 使用`println!`占位符打印值
|
||||
### 使用 `println!` 占位符打印值
|
||||
|
||||
除了位于结尾的大括号,目前为止编写的代码就只有一行代码值得讨论一下了,就是这一行:
|
||||
|
||||
```rust,ignore
|
||||
```rust ,ignore
|
||||
println!("You guessed: {}", guess);
|
||||
```
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user