mirror of
https://github.com/KaiserY/trpl-zh-cn
synced 2024-11-09 00:31:27 +08:00
check to ch03-01
This commit is contained in:
parent
2767e943b2
commit
fca0b2b4b9
@ -374,11 +374,11 @@ fn main() {
|
||||
|
||||
这里在顶部增加一行 `extern crate rand;` 通知 Rust 我们要使用外部依赖。这也会调用相应的 `use rand`,所以现在可以使用 `rand::` 前缀来调用 `rand` crate 中的任何内容。
|
||||
|
||||
接下来增加了另一行 `use`:`use rand::Rng`。`Rng` 是一个 trait,它定义了随机数生成器应实现的方法 ,想使用这些方法的话此 trait 必须在作用域中。第十章会详细介绍 trait。
|
||||
接下来增加了另一行 `use`:`use rand::Rng`。`Rng` 是一个 trait,它定义了随机数生成器应实现的方法,想使用这些方法的话此 trait 必须在作用域中。第十章会详细介绍 trait。
|
||||
|
||||
另外,中间还新增加了两行。`rand::thread_rng` 函数提供实际使用的随机数生成器:它位于当前执行线程,并从操作系统获取 seed。接下来,调用随机数生成器的 `gen_range` 方法。这个方法由刚才引入到作用域的 `Rng` trait 定义。`gen_range` 方法获取两个数字作为参数,并生成一个范围在两者之间的随机数。它包含下限但不包含上限,所以需要指定 `1` 和 `101` 来请求一个 1 和 100 之间的数。
|
||||
另外,中间还新增加了两行。`rand::thread_rng` 函数提供实际使用的随机数生成器:它位于当前执行线程本地,并从操作系统获取 seed。接下来,调用随机数生成器的 `gen_range` 方法。这个方法由刚才引入到作用域的 `Rng` trait 定义。`gen_range` 方法获取两个数字作为参数,并生成一个范围在两者之间的随机数。它包含下限但不包含上限,所以需要指定 `1` 和 `101` 来请求一个 1 和 100 之间的数。
|
||||
|
||||
知道 use 哪个 trait 和该从 crate 中调用哪个方法并不代表你 **知道** 如何使用。crate 的使用说明位于其文档中。Cargo 有一个很棒的功能是:运行 `cargo doc --open` 命令来构建所有本地依赖提供的文档,并在浏览器中打开。例如,假设你对 `rand` crate 中的其他功能感兴趣,`cargo doc --open` 并点击左侧导航栏中的 `rand`。
|
||||
知道 use 哪个 trait 和该从 crate 中调用哪个方法并不是是你唯一会 **知道** 的。crate 的使用说明位于其文档中。Cargo 有一个很棒的功能是:运行 `cargo doc --open` 命令来构建所有本地依赖提供的文档,并在浏览器中打开。例如,假设你对 `rand` crate 中的其他功能感兴趣,`cargo doc --open` 并点击左侧导航栏中的 `rand`。
|
||||
|
||||
新增加的第二行代码打印出了秘密数字。这在开发程序时很有用,因为可以测试它,不过在最终版本中会删掉它。游戏一开始就打印出结果就没什么可玩的了!
|
||||
|
||||
@ -387,6 +387,7 @@ fn main() {
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 2.53 secs
|
||||
Running `target/debug/guessing_game`
|
||||
Guess the number!
|
||||
The secret number is: 7
|
||||
@ -434,9 +435,9 @@ fn main() {
|
||||
println!("You guessed: {}", guess);
|
||||
|
||||
match guess.cmp(&secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => println!("You win!"),
|
||||
Ordering::Equal => println!("You win!"),
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -449,9 +450,9 @@ fn main() {
|
||||
|
||||
```rust,ignore
|
||||
match guess.cmp(&secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => println!("You win!"),
|
||||
Ordering::Equal => println!("You win!"),
|
||||
}
|
||||
```
|
||||
|
||||
@ -459,7 +460,7 @@ match guess.cmp(&secret_number) {
|
||||
|
||||
[match]: ch06-02-match.html
|
||||
|
||||
一个 `match` 表达式由 **分支(arms)** 构成。一个分支包含一个 **模式**(*pattern*)和表达式开头的值与分支模式相匹配时应该执行的代码。Rust 获取提供给 `match` 的值并挨个检查每个分支的模式。`match` 结构和模式是 Rust 中强大的功能,它体现了代码可能遇到的多种情形,并帮助你没有遗漏的处理。这些功能将分别在第六章和第十八章详细介绍。
|
||||
一个 `match` 表达式由 **分支(arms)** 构成。一个分支包含一个 **模式**(*pattern*)和表达式开头的值与分支模式相匹配时应该执行的代码。Rust 获取提供给 `match` 的值并挨个检查每个分支的模式。`match` 结构和模式是 Rust 中强大的功能,它体现了代码可能遇到的多种情形,并帮助你确保没有遗漏处理。这些功能将分别在第六章和第十八章详细介绍。
|
||||
|
||||
让我们看看使用 `match` 表达式的例子。假设用户猜了 50,这时随机生成的秘密数字是 38。比较 50 与 38 时,因为 50 比 38 要大,`cmp` 方法会返回 `Ordering::Greater`。`Ordering::Greater` 是 `match` 表达式得到的值。它检查第一个分支的模式,`Ordering::Less` 与 `Ordering::Greater`并不匹配,所以它忽略了这个分支的动作并来到下一个分支。下一个分支的模式是 `Ordering::Greater`,**正确** 匹配!这个分支关联的代码被执行,在屏幕打印出 `Too big!`。`match` 表达式就此终止,因为该场景下没有检查最后一个分支的必要。
|
||||
|
||||
@ -485,7 +486,6 @@ Could not compile `guessing_game`.
|
||||
|
||||
所以我们必须把从输入中读取到的 `String` 转换为一个真正的数字类型,才好与秘密数字进行比较。这可以通过在 `main` 函数体中增加如下两行代码来实现:
|
||||
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
@ -515,9 +515,9 @@ fn main() {
|
||||
println!("You guessed: {}", guess);
|
||||
|
||||
match guess.cmp(&secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => println!("You win!"),
|
||||
Ordering::Equal => println!("You win!"),
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -544,6 +544,7 @@ let guess: u32 = guess.trim().parse()
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.43 secs
|
||||
Running `target/guessing_game`
|
||||
Guess the number!
|
||||
The secret number is: 58
|
||||
@ -591,9 +592,9 @@ fn main() {
|
||||
println!("You guessed: {}", guess);
|
||||
|
||||
match guess.cmp(&secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => println!("You win!"),
|
||||
Ordering::Equal => println!("You win!"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -634,7 +635,6 @@ error: Process didn't exit successfully: `target/debug/guess` (exit code: 101)
|
||||
|
||||
让我们增加一个 `break`,在用户猜对时退出游戏:
|
||||
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
@ -665,9 +665,9 @@ fn main() {
|
||||
println!("You guessed: {}", guess);
|
||||
|
||||
match guess.cmp(&secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => {
|
||||
Ordering::Equal => {
|
||||
println!("You win!");
|
||||
break;
|
||||
}
|
||||
@ -693,7 +693,7 @@ let guess: u32 = match guess.trim().parse() {
|
||||
|
||||
如果 `parse` 能够成功的将字符串转换为一个数字,它会返回一个包含结果数字的 `Ok`。这个 `Ok` 值与 `match` 第一个分支的模式相匹配,该分支对应的动作返回 `Ok` 值中的数字 `num`,最后如愿变成新创建的 `guess` 变量。
|
||||
|
||||
如果 `parse` *不* 能将字符串转换为一个数字,它会返回一个包含更多错误信息的 `Err`。`Err` 值不能匹配第一个 `match` 分支的 `Ok(num)` 模式,但是会匹配第二个分支的 `Err(_)` 模式:`_` 是一个通配值,本例中用来匹配所有 `Err` 值,不管其中有何种信息。所以程序会执行第二个分支的动作,`continue` 意味着进入 `loop` 的下一次循环,请求另一个猜测。这样程序就有效的忽略了 `parse` 可能遇到的所有错误!
|
||||
如果 `parse` *不* 能将字符串转换为一个数字,它会返回一个包含更多错误信息的 `Err`。`Err` 值不能匹配第一个 `match` 分支的 `Ok(num)` 模式,但是会匹配第二个分支的 `Err(_)` 模式:`_` 是一个通配符值,本例中用来匹配所有 `Err` 值,不管其中有何种信息。所以程序会执行第二个分支的动作,`continue` 意味着进入 `loop` 的下一次循环,请求另一个猜测。这样程序就有效的忽略了 `parse` 可能遇到的所有错误!
|
||||
|
||||
现在万事俱备,只需运行 `cargo run`:
|
||||
|
||||
@ -751,9 +751,9 @@ fn main() {
|
||||
println!("You guessed: {}", guess);
|
||||
|
||||
match guess.cmp(&secret_number) {
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Less => println!("Too small!"),
|
||||
Ordering::Greater => println!("Too big!"),
|
||||
Ordering::Equal => {
|
||||
Ordering::Equal => {
|
||||
println!("You win!");
|
||||
break;
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
> <br>
|
||||
> commit 04aa3a45eb72855b34213703718f50a12a3eeec8
|
||||
|
||||
本章涉及一些几乎所有编程语言都有的概念,以及它们在 Rust 中是如何工作的。很多编程语言的核心概念都是共通的,本章中展示的概念都不是 Rust 特有的,不过我们会在 Rust 环境中讨论它们,解释它们的使用习惯。
|
||||
本章涉及一些几乎所有编程语言都有的概念,以及它们在 Rust 中是如何工作的。很多编程语言的核心概念都是共通的,本章中展示的概念都不是 Rust 所特有的,不过我们会在 Rust 环境中讨论它们,解释它们的使用习惯。
|
||||
|
||||
具体地,我们将会学习变量,基本类型,函数,注释和控制流。这些基础知识将会出现在每一个 Rust 程序中,提早学习这些概念会为你奠定坚实的起步基础。
|
||||
|
||||
|
@ -2,9 +2,9 @@
|
||||
|
||||
> [ch03-01-variables-and-mutability.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch03-01-variables-and-mutability.md)
|
||||
> <br>
|
||||
> commit d06a6a181fd61704cbf7feb55bc61d518c6469f9
|
||||
> commit 6aad5008b69078a2fc18e6dd7e00ef395170c749
|
||||
|
||||
第二章中提到过,变量默认是 **不可变**(*immutable*)的。这是利用 Rust 安全和简单并发的优势编写代码一大助力。不过,你仍然可以使用可变变量。让我们探讨一下 Rust 拥抱不可变性的原因及方法,以及何时你不想使用不可变性。
|
||||
第二章中提到过,变量默认是 **不可变**(*immutable*)的。这是鼓励你利用 Rust 安全和简单并发的优势来编写代码的一大助力。不过,你仍然可以使用可变变量。让我们探讨一下 Rust 拥抱不可变性的原因及方法,以及何时你不想使用不可变性。
|
||||
|
||||
当变量不可变时,意味着一旦值被绑定上一个名称,你就不能改变这个值。作为说明,通过 `cargo new --bin variables` 在 *projects* 目录生成一个叫做 *variables* 的新项目。
|
||||
|
||||
@ -24,23 +24,23 @@ fn main() {
|
||||
保存并使用 `cargo run` 运行程序。应该会看到一个错误信息,如下输出所示:
|
||||
|
||||
```text
|
||||
error[E0384]: re-assignment of immutable variable `x`
|
||||
error[E0384]: cannot assign twice to immutable variable `x`
|
||||
--> src/main.rs:4:5
|
||||
|
|
||||
2 | let x = 5;
|
||||
| - first assignment to `x`
|
||||
3 | println!("The value of x is: {}", x);
|
||||
4 | x = 6;
|
||||
| ^^^^^ re-assignment of immutable variable
|
||||
| ^^^^^ cannot assign twice to immutable variable
|
||||
```
|
||||
|
||||
这个例子展示了编译器如何帮助你找出程序中的错误。虽然编译错误令人沮丧,那也不过是说程序不能安全的完成你想让它完成的工作;而 **不能** 说明你是不是一个好程序员!有经验的 Rustacean 们一样会遇到编译错误。
|
||||
|
||||
这些错误给出的原因是 `对不可变变量重新赋值`(`re-assignment of immutable variable`),因为我们尝试对不可变变量 `x` 赋第二个值。
|
||||
这些错误给出的原因是 `不能对不可变变量二次赋值`(`cannot assign twice to immutable variable x`),因为我们尝试对不可变变量 `x` 赋第二个值。
|
||||
|
||||
在尝试改变预设为不可变的值的时候产生编译错误是很重要的,因为这种情况可能导致 bug:如果代码的一部分假设一个值永远也不会改变,而另一部分代码改变了它,第一部分代码就有可能以不可预料的方式运行。不得不承认这种 bug 难以跟踪,尤其是第二部分代码只是 **有时** 改变其值。
|
||||
在尝试改变预设为不可变的值的时候产生编译错误是很重要的,因为这种情况可能导致 bug:如果代码的一部分假设一个值永远也不会改变,而另一部分代码改变了它,第一部分代码就有可能以不可预料的方式运行。不得不承认这种 bug 难以跟踪,尤其是第二部分代码只是 **有时** 改变其值的时候。
|
||||
|
||||
Rust 编译器保证,如果声明一个值不会变,它就真的不会变。这意味着当阅读和编写代码时,不需要记住如何以及哪里可能会被改变,从而使得代码易于推导。
|
||||
Rust 编译器保证,如果声明一个值不会变,它就真的不会变。这意味着当阅读和编写代码时,不需要追踪一个值如何以及哪里可能会被改变,从而使得代码易于推导。
|
||||
|
||||
不过可变性也是非常有用的。变量只是默认不可变,可以通过在变量名之前加 `mut` 来使其可变。除了使值可以改变之外,它向读者表明了其他代码将会改变这个变量的意图。
|
||||
|
||||
@ -62,12 +62,13 @@ fn main() {
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling variables v0.1.0 (file:///projects/variables)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.30 secs
|
||||
Running `target/debug/variables`
|
||||
The value of x is: 5
|
||||
The value of x is: 6
|
||||
```
|
||||
|
||||
通过 `mut`,允许把绑定到 `x` 的值从 `5` 改成 `6`。在一些情况下,你会想用可变变量,因为这样的代码比起只用不可变变量的更容易编写。
|
||||
通过 `mut`,允许把绑定到 `x` 的值从 `5` 改成 `6`。在一些情况下,你会想用可变变量,因为这样的代码比起只用不可变变量的实现更容易编写。
|
||||
|
||||
除了避免 bug 外,还有很多地方需要权衡取舍。例如,使用大型数据结构时,适当地使用可变变量,可能比复制和返回新分配的实例更快。对于较小的数据结构,总是创建新实例,采用更偏向函数式的风格编程,可能会使代码更易理解,为可读性而遭受性能惩罚或许值得。
|
||||
|
||||
@ -77,7 +78,7 @@ The value of x is: 6
|
||||
|
||||
首先,不允许对常量使用 `mut`:常量不光默认不能变,它总是不能变。
|
||||
|
||||
声明常量使用 `const` 关键字而不是 `let`,而且 *必须* 注明值的类型。在下一部分,“数据类型”,涉及到类型和类型注解,现在无需关心这些细节,记住总是标注类型即可。
|
||||
声明常量使用 `const` 关键字而不是 `let`,并且 *必须* 注明值的类型。在下一部分,“数据类型” 中会涉及到类型和类型注解,现在无需关心这些细节,记住总是标注类型即可。
|
||||
|
||||
常量可以在任何作用域声明,包括全局作用域,这在一个值需要被很多部分的代码用到时很有用。
|
||||
|
||||
@ -91,7 +92,7 @@ const MAX_POINTS: u32 = 100_000;
|
||||
|
||||
在声明它的作用域之中,常量在整个程序生命周期中都有效,这使得常量可以作为多处代码使用的全局范围的值,例如一个游戏中所有玩家可以获取的最高分或者光速。
|
||||
|
||||
将用于整个程序的硬编码的值声明为常量对后来的维护者了解值的意义很有帮助。它也能将硬编码的值汇总一处,为将来可能的修改提供方便。
|
||||
将用于整个程序的硬编码的值声明为常量对后来的维护者了解值的意义很有帮助。同时将硬编码的值汇总于一处,也能为将来修改提供方便。
|
||||
|
||||
### 隐藏(Shadowing)
|
||||
|
||||
@ -116,6 +117,7 @@ fn main() {
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling variables v0.1.0 (file:///projects/variables)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
|
||||
Running `target/debug/variables`
|
||||
The value of x is: 12
|
||||
```
|
||||
@ -149,4 +151,4 @@ error[E0308]: mismatched types
|
||||
found type `usize`
|
||||
```
|
||||
|
||||
现在我们已经了解了变量如何工作,让我们再看看更多的数据类型。
|
||||
现在我们已经了解了变量如何工作,让我们再看看更多变量可以拥有的数据类型。
|
||||
|
Loading…
Reference in New Issue
Block a user