update to ch03-05

pull/584/head
KaiserY 3 years ago
parent 881c440e8b
commit 23278a0c39

@ -3,9 +3,6 @@ title = "Rust 程序设计语言 简体中文版"
author = "Steve Klabnik 和 Carol Nichols以及来自 Rust 社区的贡献Rust 中文社区翻译)"
description = "Rust 程序设计语言 简体中文版"
[build-dir]
destination = "mdbook"
[output.html]
additional-css = ["ferris.css"]
additional-css = ["ferris.css", "theme/2018-edition.css"]
additional-js = ["ferris.js"]

@ -19,13 +19,25 @@ body.ayu .not_desired_behavior {
background: #501f21;
}
.ferris {
.ferris-container {
position: absolute;
z-index: 99;
right: 5px;
top: 30px;
width: 10%;
height: auto;
}
.ferris {
vertical-align: top;
margin-left: 0.2em;
height: auto;
}
.ferris-large {
width: 4.5em;
}
.ferris-small {
width: 2.3em;
}
.ferris-explain {

@ -19,19 +19,36 @@ document.addEventListener('DOMContentLoaded', () => {
}
})
function attachFerrises (type) {
function attachFerrises(type) {
var elements = document.getElementsByClassName(type.attr)
for (var codeBlock of elements) {
var lines = codeBlock.textContent.split(/\r|\r\n|\n/).length - 1;
if (lines >= 4) {
attachFerris(codeBlock, type)
var lines = codeBlock.innerText.replace(/\n$/, '').split(/\n/).length
var size = 'large'
if (lines < 4) {
size = 'small'
}
var container = prepareFerrisContainer(codeBlock, size == 'small')
container.appendChild(createFerris(type, size))
}
}
function prepareFerrisContainer(element, useButtons) {
var foundButtons = element.parentElement.querySelector('.buttons')
if (useButtons && foundButtons) {
return foundButtons
}
var div = document.createElement('div')
div.classList.add('ferris-container')
element.parentElement.insertBefore(div, element)
return div
}
function attachFerris (element, type) {
function createFerris(type, size) {
var a = document.createElement('a')
a.setAttribute('href', 'ch00-00-introduction.html#ferris')
a.setAttribute('target', '_blank')
@ -39,9 +56,10 @@ function attachFerris (element, type) {
var img = document.createElement('img')
img.setAttribute('src', 'img/ferris/' + type.attr + '.svg')
img.setAttribute('title', type.title)
img.className = 'ferris'
img.classList.add('ferris')
img.classList.add('ferris-' + size)
a.appendChild(img)
element.parentElement.insertBefore(a, element)
return a
}

@ -1,17 +1,17 @@
fn main() {
// addition
// 加法
let sum = 5 + 10;
// subtraction
// 减法
let difference = 95.5 - 4.3;
// multiplication
// 乘法
let product = 4 * 30;
// division
// 除法
let quotient = 56.7 / 32.2;
let floored = 2 / 3; // Results in 0
let floored = 2 / 3; // 结果为 0
// remainder
// 取余
let remainder = 43 % 5;
}

@ -1,5 +1,5 @@
fn main() {
let t = true;
let f: bool = false; // with explicit type annotation
let f: bool = false; // 显式指定类型注解
}

@ -1,6 +1,6 @@
# 编写 猜猜看 游戏
> [ch02-00-guessing-game-tutorial.md](https://github.com/rust-lang/book/blob/master/src/ch02-00-guessing-game-tutorial.md) > <br>
> [ch02-00-guessing-game-tutorial.md](https://github.com/rust-lang/book/blob/main/src/ch02-00-guessing-game-tutorial.md) > <br>
> commit d68d96576b705fcff7aa6341a9840f4de3c0ca0c
让我们一起动手完成一个项目,来快速上手 Rust本章将介绍 Rust 中一些常用概念,并通过真实的程序来展示如何运用它们。你将会学到 `let`、`match`、方法method、关联函数associated function、使用外部 crate 等知识!后续章节会深入探讨这些概念的细节。在这一章,我们将练习基础内容。

@ -11,4 +11,4 @@
> ## 关键字
> Rust 语言有一组保留的 **关键字***keywords*),就像大部分语言一样,它们只能由语言本身使用。记住,你不能使用这些关键字作为变量或函数的名称。大部分关键字有特殊的意义,你将在 Rust 程序中使用它们完成各种任务;一些关键字目前没有相应的功能,是为将来可能添加的功能保留的。可以在[附录 A][appendix_a]<!-- ignore --> 中找到关键字的列表。
[appendix_a]: appendix-01-keywords.md
[appendix_a]: appendix-01-keywords.md

@ -2,7 +2,7 @@
> [ch03-01-variables-and-mutability.md](https://github.com/rust-lang/book/blob/main/src/ch03-01-variables-and-mutability.md)
> <br>
> commit d281b7b062e6dbfbcf47f8381073f7fce9e5cd4e
> commit 059f85014f2a96b7a2dcdc23e01c87ae319873bc
正如第二章中[“使用变量储存值”][storing-values-with-variables]<!-- ignore --> 部分提到的那样变量默认是不可改变的immutable。这是 Rust 提供给你的众多优势之一,让你得以充分利用 Rust 提供的安全性和简单并发性来编写代码。不过,你仍然可以使用可变变量。让我们探讨一下 Rust 为何及如何鼓励你利用不可变性,以及何时你会选择不使用不可变性。
@ -13,37 +13,13 @@
<span class="filename">文件名: src/main.rs</span>
```rust,ignore,does_not_compile
fn main() {
let x = 5;
println!("The value of x is: {}", x);
x = 6;
println!("The value of x is: {}", x);
}
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-01-variables-are-immutable/src/main.rs}}
```
保存并使用 `cargo run` 运行程序。应该会看到一条错误信息,如下输出所示:
```console
$ cargo run
Compiling variables v0.1.0 (file:///projects/variables)
error[E0384]: cannot assign twice to immutable variable `x`
--> src/main.rs:4:5
|
2 | let x = 5;
| -
| |
| first assignment to `x`
| help: consider making this binding mutable: `mut x`
3 | println!("The value of x is: {}", x);
4 | x = 6;
| ^^^^^ cannot assign twice to immutable variable
error: aborting due to previous error
For more information about this error, try `rustc --explain E0384`.
error: could not compile `variables`
To learn more, run the command again with --verbose.
{{#include ../listings/ch03-common-programming-concepts/no-listing-01-variables-are-immutable/output.txt}}
```
这个例子展示了编译器如何帮助你找出程序中的错误。虽然编译错误令人沮丧,但那只是表示程序不能安全的完成你想让它完成的工作;并 **不能** 说明你不是一个好程序员!经验丰富的 Rustacean 们一样会遇到编译错误。
@ -61,23 +37,13 @@ Rust 编译器保证,如果声明一个值不会变,它就真的不会变,
<span class="filename">文件名: src/main.rs</span>
```rust
fn main() {
let mut x = 5;
println!("The value of x is: {}", x);
x = 6;
println!("The value of x is: {}", x);
}
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-02-adding-mut/src/main.rs}}
```
现在运行这个程序,出现如下内容:
```console
$ cargo run
Compiling variables v0.1.0 (file:///projects/variables)
Finished dev [unoptimized + debuginfo] target(s) in 0.30s
Running `target/debug/variables`
The value of x is: 5
The value of x is: 6
{{#include ../listings/ch03-common-programming-concepts/no-listing-02-adding-mut/output.txt}}
```
通过 `mut`,允许把绑定到 `x` 的值从 `5` 改成 `6`。除了防止出现 bug 外,还有很多地方需要权衡取舍。例如,使用大型数据结构时,适当地使用可变变量,可能比复制和返回新分配的实例更快。对于较小的数据结构,总是创建新实例,采用更偏向函数式的编程风格,可能会使代码更易理解,为可读性而牺牲性能或许是值得的。
@ -113,29 +79,13 @@ const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
<span class="filename">文件名: src/main.rs</span>
```rust
fn main() {
let x = 5;
let x = x + 1;
{
let x = x * 2;
println!("The value of x in the inner scope is: {}", x);
}
println!("The value of x is: {}", x);
}
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-03-shadowing/src/main.rs}}
```
这个程序首先将 `x` 绑定到值 `5` 上。接着通过 `let x =` 隐藏 `x`,获取初始值并加 `1`,这样 `x` 的值就变成 `6` 了。然后,在内部作用域内,第三个 `let` 语句也隐藏了 `x`,将之前的值乘以 `2``x` 得到的值是 `12`。当该作用域结束时,内部 shadowing 的作用域也结束了,`x` 又返回到 `6`。运行这个程序,它会有如下输出:
```console
$ cargo run
Compiling variables v0.1.0 (file:///projects/variables)
Finished dev [unoptimized + debuginfo] target(s) in 0.31s
Running `target/debug/variables`
The value of x in the inner scope is: 12
The value of x is: 6
{{#include ../listings/ch03-common-programming-concepts/no-listing-03-shadowing/output.txt}}
```
隐藏与将变量标记为 `mut` 是有区别的。当不小心尝试对变量重新赋值时,如果没有使用 `let` 关键字,就会导致编译时错误。通过使用 `let`,我们可以用这个值进行一些计算,不过计算完之后变量仍然是不可变的。
@ -143,34 +93,19 @@ The value of x is: 6
`mut` 与隐藏的另一个区别是,当再次使用 `let` 时,实际上创建了一个新变量,我们可以改变值的类型,并且复用这个名字。例如,假设程序请求用户输入空格字符来说明希望在文本之间显示多少个空格,接下来我们想将输入存储成数字(多少个空格):
```rust
let spaces = " ";
let spaces = spaces.len();
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-04-shadowing-can-change-types/src/main.rs:here}}
```
第一个 `spaces` 变量是字符串类型,第二个 `spaces` 变量是数字类型。隐藏使我们不必使用不同的名字,如 `spaces_str``spaces_num`;相反,我们可以复用 `spaces` 这个更简单的名字。然而,如果尝试使用 `mut`,将会得到一个编译时错误,如下所示:
```rust,ignore,does_not_compile
let mut spaces = " ";
spaces = spaces.len();
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-05-mut-cant-change-types/src/main.rs:here}}
```
这个错误说明,我们不能改变变量的类型:
```console
$ cargo run
Compiling variables v0.1.0 (file:///projects/variables)
error[E0308]: mismatched types
--> src/main.rs:3:14
|
3 | spaces = spaces.len();
| ^^^^^^^^^^^^ expected `&str`, found `usize`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.
error: could not compile `variables`
To learn more, run the command again with --verbose.
{{#include ../listings/ch03-common-programming-concepts/no-listing-05-mut-cant-change-types/output.txt}}
```
现在我们已经了解了变量如何工作,让我们看看变量可以拥有的更多数据类型。

@ -15,20 +15,7 @@ let guess: u32 = "42".parse().expect("Not a number!");
这里如果不添加类型注解Rust 会显示如下错误,这说明编译器需要我们提供更多信息,来了解我们想要的类型:
```console
$ cargo build
Compiling no_type_annotations v0.1.0 (file:///projects/no_type_annotations)
error[E0282]: type annotations needed
--> src/main.rs:2:9
|
2 | let guess = "42".parse().expect("Not a number!");
| ^^^^^ consider giving `guess` a type
error: aborting due to previous error
For more information about this error, try `rustc --explain E0282`.
error: could not compile `no_type_annotations`
To learn more, run the command again with --verbose.
{{#include ../listings/ch03-common-programming-concepts/output-only-01-no-type-annotations/output.txt}}
```
你会看到其它数据类型的各种类型注解。
@ -92,11 +79,7 @@ Rust 也有两个原生的 **浮点数***floating-point numbers*)类型,
<span class="filename">文件名: src/main.rs</span>
```rust
fn main() {
let x = 2.0; // f64
let y: f32 = 3.0; // f32
}
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-06-floating-point/src/main.rs}}
```
浮点数采用 IEEE-754 标准表示。`f32` 是单精度浮点数,`f64` 是双精度浮点数。
@ -108,23 +91,7 @@ Rust 中的所有数字类型都支持基本数学运算:加法、减法、乘
<span class="filename">文件名: src/main.rs</span>
```rust
fn main() {
// 加法
let sum = 5 + 10;
// 减法
let difference = 95.5 - 4.3;
// 乘法
let product = 4 * 30;
// 除法
let quotient = 56.7 / 32.2;
let floored = 2 / 3; // 结果为 0
// 取余
let remainder = 43 % 5;
}
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-07-numeric-operations/src/main.rs}}
```
这些语句中的每个表达式使用了一个数学运算符并计算出了一个值,然后绑定给一个变量。[附录 B][appendix_b]<!-- ignore --> 包含 Rust 提供的所有运算符的列表。
@ -136,11 +103,7 @@ fn main() {
<span class="filename">文件名: src/main.rs</span>
```rust
fn main() {
let t = true;
let f: bool = false; // 显式指定类型注解
}
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-08-boolean/src/main.rs}}
```
使用布尔值的主要场景是条件表达式,例如 `if` 表达式。在 [“控制流”“Control Flow”][control-flow] 部分将介绍 `if` 表达式在 Rust 中如何工作。
@ -152,11 +115,7 @@ Rust的 `char` 类型是语言中最原生的字母类型。下面是一些声
<span class="filename">文件名: src/main.rs</span>
```rust
fn main() {
let c = 'z';
let z = '';
let heart_eyed_cat = '😻';
}
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-09-char/src/main.rs}}
```
注意,我们用单引号声明 `char` 字面量而与之相反的是使用双引号声明字符串字面量。Rust 的 `char` 类型的大小为四个字节(four bytes),并代表了一个 Unicode 标量值Unicode Scalar Value这意味着它可以比 ASCII 表示更多内容。在 Rust 中拼音字母Accented letters中文、日文、韩文等字符emoji绘文字以及零长度的空白字符都是有效的 `char` 值。Unicode 标量值包含从 `U+0000``U+D7FF``U+E000``U+10FFFF` 在内的值。不过,“字符” 并不是一个 Unicode 中的概念,所以人直觉上的 “字符” 可能与 Rust 中的 `char` 并不符合。第八章的 [“使用字符串存储 UTF-8 编码的文本”][strings] 中将详细讨论这个主题。
@ -174,9 +133,7 @@ fn main() {
<span class="filename">文件名: src/main.rs</span>
```rust
fn main() {
let tup: (i32, f64, u8) = (500, 6.4, 1);
}
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-10-tuples/src/main.rs}}
```
`tup` 变量绑定到整个元组上因为元组是一个单独的复合元素。为了从元组中获取单个值可以使用模式匹配pattern matching来解构destructure元组值像这样
@ -184,13 +141,7 @@ fn main() {
<span class="filename">文件名: src/main.rs</span>
```rust
fn main() {
let tup = (500, 6.4, 1);
let (x, y, z) = tup;
println!("The value of y is: {}", y);
}
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-11-destructuring-tuples/src/main.rs}}
```
程序首先创建了一个元组并绑定到 `tup` 变量上。接着使用了 `let` 和一个模式将 `tup` 分成了三个不同的变量,`x`、`y` 和 `z`。这叫做 **解构***destructuring*),因为它将一个元组拆成了三个部分。最后,程序打印出了 `y` 的值,也就是 `6.4`
@ -200,15 +151,7 @@ fn main() {
<span class="filename">文件名: src/main.rs</span>
```rust
fn main() {
let x: (i32, f64, u8) = (500, 6.4, 1);
let five_hundred = x.0;
let six_point_four = x.1;
let one = x.2;
}
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-12-tuple-indexing/src/main.rs}}
```
这个程序创建了一个元组,`x`,并接着使用索引为每个元素创建新变量。跟大多数编程语言一样,元组的第一个索引值是 0。
@ -224,9 +167,7 @@ fn main() {
<span class="filename">文件名: src/main.rs</span>
```rust
fn main() {
let a = [1, 2, 3, 4, 5];
}
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-13-arrays/src/main.rs}}
```
当你想要在栈stack而不是在堆heap上为数据分配空间[第四章][stack-and-heap]将讨论栈与堆的更多内容),或者是想要确保总是有固定数量的元素时,数组非常有用。但是数组并不如 vector 类型灵活。vector 类型是标准库提供的一个 **允许** 增长和缩小长度的类似数组的集合类型。当不确定是应该使用数组还是 vector 的时候,那么很可能应该使用 vector。[第八章][vectors]会详细讨论 vector。
@ -261,12 +202,7 @@ let a = [3; 5];
<span class="filename">文件名: src/main.rs</span>
```rust
fn main() {
let a = [1, 2, 3, 4, 5];
let first = a[0];
let second = a[1];
}
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-14-array-indexing/src/main.rs}}
```
在这个例子中,叫做 `first` 的变量的值是 `1`,因为它是数组索引 `[0]` 的值。变量 `second` 将会是数组索引 `[1]` 的值 `2`
@ -278,38 +214,14 @@ fn main() {
<span class="filename">文件名: src/main.rs</span>
```rust,ignore,panics
use std::io;
fn main() {
let a = [1, 2, 3, 4, 5];
println!("Please enter an array index.");
let mut index = String::new();
io::stdin()
.read_line(&mut index)
.expect("Failed to read line");
let index: usize = index
.trim()
.parse()
.expect("Index entered was not a number");
let element = a[index];
println!(
"The value of the element at index {} is: {}",
index, element
);
}
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-15-invalid-array-access/src/main.rs}}
```
此代码编译成功。如果您使用 `cargo run` 运行此代码并输入 0、1、2、3 或 4程序将在数组中的索引处打印出相应的值。如果你输入一个超过数组末端的数字如 10你会看到这样的输出
```console
thread 'main' panicked at 'index out of bounds: the len is 5 but the index is 10', src/main.rs:19:19
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrac
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
```
程序在索引操作中使用一个无效的值时导致 **运行时** 错误。程序带着错误信息退出,并且没有执行最后的 `println!` 语句。当尝试用索引访问一个元素时Rust 会检查指定的索引是否小于数组的长度。如果索引超出了数组长度Rust 会 *panic*,这是 Rust 术语,它用于程序因为错误而退出的情况。这种检查必须在运行时进行,特别是在这种情况下,因为编译器不可能知道用户在以后运行代码时将输入什么值。

@ -2,7 +2,7 @@
> [ch03-03-how-functions-work.md](https://github.com/rust-lang/book/blob/main/src/ch03-03-how-functions-work.md)
> <br>
> commit 1b8746013079f2e2ce1c8e85f633d9769778ea7f
> commit 3cb562efb67fd5b57c0b20c316cbb8179133e196
函数在 Rust 代码中非常普遍。你已经见过语言中最重要的函数之一:`main` 函数,它是很多程序的入口点。你也见过 `fn` 关键字,它用来声明新函数。
@ -11,15 +11,7 @@ Rust 代码中的函数和变量名使用 *snake case* 规范风格。在 snake
<span class="filename">文件名: src/main.rs</span>
```rust
fn main() {
println!("Hello, world!");
another_function();
}
fn another_function() {
println!("Another function.");
}
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-16-functions/src/main.rs}}
```
我们在Rust 中通过输入 `fn` 后面跟着函数名和一对圆括号来定义函数。大括号告诉编译器哪里是函数体的开始和结尾。
@ -28,13 +20,8 @@ fn another_function() {
让我们新建一个叫做 *functions* 的二进制项目来进一步探索函数。将上面的 `another_function` 例子写入 *src/main.rs* 中并运行。你应该会看到如下输出:
```text
$ cargo run
Compiling functions v0.1.0 (file:///projects/functions)
Finished dev [unoptimized + debuginfo] target(s) in 0.28s
Running `target/debug/functions`
Hello, world!
Another function.
```console
{{#include ../listings/ch03-common-programming-concepts/no-listing-16-functions/output.txt}}
```
`main` 函数中的代码会按顺序执行。首先,打印 “Hello, world!” 信息,然后调用 `another_function` 函数并打印它的信息。
@ -48,53 +35,33 @@ Another function.
<span class="filename">文件名: src/main.rs</span>
```rust
fn main() {
another_function(5);
}
fn another_function(x: i32) {
println!("The value of x is: {}", x);
}
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-17-functions-with-parameters/src/main.rs}}
```
尝试运行程序,将会输出如下内容:
```text
$ cargo run
Compiling functions v0.1.0 (file:///projects/functions)
Finished dev [unoptimized + debuginfo] target(s) in 1.21s
Running `target/debug/functions`
The value of x is: 5
```console
{{#include ../listings/ch03-common-programming-concepts/no-listing-17-functions-with-parameters/output.txt}}
```
`another_function` 的声明中有一个命名为 `x` 的参数。`x` 的类型被指定为 `i32`。当我们将 `5` 传给 `another_function` 时,`println!` 宏将 `5` 放入格式化字符串中大括号的位置。
在函数签名中,**必须** 声明每个参数的类型。这是 Rust 设计中一个经过慎重考虑的决定:要求在函数定义中提供类型注解,意味着编译器不需要你在代码的其他地方注明类型来指出你的意图。
在函数签名中,**必须** 声明每个参数的类型。这是 Rust 设计中一个经过慎重考虑的决定:要求在函数定义中提供类型注解,意味着编译器再也不需要你在代码的其他地方注明类型来指出你的意图。
当定义多个参数时,使用逗号分隔,像这样:
<span class="filename">文件名: src/main.rs</span>
```rust
fn main() {
print_labeled_measurement(5, 'h');
}
fn print_labeled_measurement(value: i32, unit_label: char) {
println!("The measurement is: {}{}", value, unit_label);
}
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-18-functions-with-multiple-parameters/src/main.rs}}
```
这个例子创建了一个名为 `print_labeled_measurement` 的函数,它有两个参数。第一个参数名为 `value` 类型是 `i32`。第二个参数是 `unit_label` ,类型是 `char`。然后,该函数打印包含 `value``unit_label` 的文本。
尝试运行代码。使用上面的例子替换当前 *functions* 项目的 *src/main.rs* 文件,并用 `cargo run` 运行它:
```text
$ cargo run
Compiling functions v0.1.0 (file:///projects/functions)
Finished dev [unoptimized + debuginfo] target(s) in 0.31s
Running `target/debug/functions`
The measurement is: 5h
```console
{{#include ../listings/ch03-common-programming-concepts/no-listing-18-functions-with-multiple-parameters/output.txt}}
```
因为我们使用 `5` 作为 `value` 的值,`h` 作为 `unit_label` 的值来调用函数,所以程序输出包含这些值。
@ -110,9 +77,7 @@ The measurement is: 5h
<span class="filename">文件名: src/main.rs</span>
```rust
fn main() {
let y = 6;
}
{{#rustdoc_include ../listings/ch03-common-programming-concepts/listing-03-01/src/main.rs}}
```
<span class="caption">列表 3-1包含一个语句的 `main` 函数定义</span>
@ -124,47 +89,13 @@ fn main() {
<span class="filename">文件名: src/main.rs</span>
```rust,ignore,does_not_compile
fn main() {
let x = (let y = 6);
}
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-19-statements-vs-expressions/src/main.rs}}
```
当运行这个程序时,会得到如下错误:
```text
$ cargo run
Compiling functions v0.1.0 (file:///projects/functions)
error[E0658]: `let` expressions in this position are experimental
--> src/main.rs:2:14
|
2 | let x = (let y = 6);
| ^^^^^^^^^
|
= note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error: expected expression, found statement (`let`)
--> src/main.rs:2:14
|
2 | let x = (let y = 6);
| ^^^^^^^^^
|
= note: variable declaration using `let` is a statement
warning: unnecessary parentheses around assigned value
--> src/main.rs:2:13
|
2 | let x = (let y = 6);
| ^^^^^^^^^^^ help: remove these parentheses
|
= note: `#[warn(unused_parens)]` on by default
error: aborting due to 2 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0658`.
error: could not compile `functions`
To learn more, run the command again with --verbose.
```console
{{#include ../listings/ch03-common-programming-concepts/no-listing-19-statements-vs-expressions/output.txt}}
```
`let y = 6` 语句并不返回值,所以没有可以绑定到 `x` 上的值。这与其他语言不同,例如 C 和 Ruby它们的赋值语句会返回所赋的值。在这些语言中可以这么写 `x = y = 6`,这样 `x``y` 的值都是 `6`Rust 中不能这样写。
@ -174,16 +105,7 @@ To learn more, run the command again with --verbose.
<span class="filename">文件名: src/main.rs</span>
```rust
fn main() {
let x = 5;
let y = {
let x = 3;
x + 1
};
println!("The value of y is: {}", y);
}
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-20-blocks-are-expressions/src/main.rs}}
```
这个表达式:
@ -204,25 +126,13 @@ fn main() {
<span class="filename">文件名: src/main.rs</span>
```rust
fn five() -> i32 {
5
}
fn main() {
let x = five();
println!("The value of x is: {}", x);
}
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-21-function-return-values/src/main.rs}}
```
`five` 函数中没有函数调用、宏、甚至没有 `let` 语句——只有数字 `5`。这在 Rust 中是一个完全有效的函数。注意,也指定了函数返回值的类型,就是 `-> i32`。尝试运行代码;输出应该看起来像这样:
`five` 函数中没有函数调用、宏、甚至没有 `let` 语句 —— 只有数字 `5`。这在 Rust 中是一个完全有效的函数。注意,也指定了函数返回值的类型,就是 `-> i32`。尝试运行代码;输出应该看起来像这样:
```text
$ cargo run
Compiling functions v0.1.0 (file:///projects/functions)
Finished dev [unoptimized + debuginfo] target(s) in 0.30s
Running `target/debug/functions`
The value of x is: 5
```console
{{#include ../listings/ch03-common-programming-concepts/no-listing-21-function-return-values/output.txt}}
```
`five` 函数的返回值是 `5`,所以返回值类型是 `i32`。让我们仔细检查一下这段代码。有两个重要的部分:首先,`let x = five();` 这一行表明我们使用函数的返回值初始化一个变量。因为 `five` 函数返回 `5`,这一行与如下代码相同:
@ -238,15 +148,7 @@ let x = 5;
<span class="filename">文件名: src/main.rs</span>
```rust
fn main() {
let x = plus_one(5);
println!("The value of x is: {}", x);
}
fn plus_one(x: i32) -> i32 {
x + 1
}
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-22-function-parameter-and-return/src/main.rs}}
```
运行代码会打印出 `The value of x is: 6`。但如果在包含 `x + 1` 的行尾加上一个分号,把它从表达式变成语句,我们将看到一个错误。
@ -254,38 +156,13 @@ fn plus_one(x: i32) -> i32 {
<span class="filename">文件名: src/main.rs</span>
```rust,ignore,does_not_compile
fn main() {
let x = plus_one(5);
println!("The value of x is: {}", x);
}
fn plus_one(x: i32) -> i32 {
x + 1;
}
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-23-statements-dont-return-values/src/main.rs}}
```
运行代码会产生一个错误,如下:
```text
$ cargo run
Compiling functions v0.1.0 (file:///projects/functions)
error[E0308]: mismatched types
--> src/main.rs:7:24
|
7 | fn plus_one(x: i32) -> i32 {
| -------- ^^^ expected `i32`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression
8 | x + 1;
| - help: consider removing this semicolon
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.
error: could not compile `functions`
To learn more, run the command again with --verbose.
```console
{{#include ../listings/ch03-common-programming-concepts/no-listing-23-statements-dont-return-values/output.txt}}
```
主要的错误信息“mismatched types”类型不匹配揭示了代码的核心问题。函数 `plus_one` 的定义说明它要返回一个 `i32` 类型的值,不过语句并不会返回值,使用单位类型 `()` 表示不返回值。因为不返回值与函数定义相矛盾从而出现一个错误。在输出中Rust 提供了一条信息,可能有助于纠正这个错误:它建议删除分号,这会修复这个错误。

@ -2,9 +2,9 @@
> [ch03-04-comments.md](https://github.com/rust-lang/book/blob/main/src/ch03-04-comments.md)
> <br>
> commit 25a1530ccbf0a79c8df2920ee2af8beb106122e8
> commit d281b7b062e6dbfbcf47f8381073f7fce9e5cd4e
所有程序员都力求使其代码易于理解,不过有时还需要提供额外的解释。在这种情况下,程序员在源码中留下记录,或者 **注释***comments*),编译器会忽略它们,不过阅读代码的人可能觉得有用。
所有程序员都力求使其代码易于理解,不过有时还需要提供额外的解释。在这种情况下,程序员在源码中留下 **注释***comments*),编译器会忽略它们,不过阅读代码的人可能觉得有用。
这是一个简单的注释:
@ -25,9 +25,7 @@
<span class="filename">文件名: src/main.rs</span>
```rust
fn main() {
let lucky_number = 7; // Im feeling lucky today
}
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-24-comments-end-of-line/src/main.rs}}
```
不过你更经常看到的是以这种格式使用它们,也就是位于它所解释的代码行的上面一行:
@ -35,10 +33,7 @@ fn main() {
<span class="filename">文件名: src/main.rs</span>
```rust
fn main() {
// Im feeling lucky today
let lucky_number = 7;
}
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-25-comments-above-line/src/main.rs}}
```
Rust 还有另一种注释,称为文档注释,我们将在 14 章的 “将 crate 发布到 Crates.io” 部分讨论它。

@ -2,9 +2,9 @@
> [ch03-05-control-flow.md](https://github.com/rust-lang/book/blob/main/src/ch03-05-control-flow.md)
> <br>
> commit 4b86611b0e63151f6e166edc9ecf870d553e1f09
> commit 1b8746013079f2e2ce1c8e85f633d9769778ea7f
根据条件是否为真来决定是否执行某些代码以及根据条件是否为真来重复运行一段代码是大部分编程语言的基本组成部分。Rust 代码中最常见的用来控制执行流的结构是 `if` 表达式和循环。
根据条件是否为真来决定是否执行某些代码,以及根据条件是否为真来重复运行一段代码的能力是大部分编程语言的基本组成部分。Rust 代码中最常见的用来控制执行流的结构是 `if` 表达式和循环。
### `if` 表达式
@ -15,19 +15,9 @@
<span class="filename">文件名: src/main.rs</span>
```rust
fn main() {
let number = 3;
if number < 5 {
println!("condition was true");
} else {
println!("condition was false");
}
}
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-26-if-true/src/main.rs}}
```
<!-- NEXT PARAGRAPH WRAPPED WEIRD INTENTIONALLY SEE #199 -->
所有的 `if` 表达式都以 `if` 关键字开头,其后跟一个条件。在这个例子中,条件检查变量 `number` 的值是否小于 5。在条件为真时希望执行的代码块位于紧跟条件之后的大括号中。`if` 表达式中与条件关联的代码块有时被叫做 *arms*,就像第二章 [“比较猜测的数字和秘密数字”][comparing-the-guess-to-the-secret-number] 部分中讨论到的 `match` 表达式中的分支一样。
也可以包含一个可选的 `else` 表达式来提供一个在条件为假时应当执行的代码块,这里我们就这么做了。如果不提供 `else` 表达式并且条件为假时,程序会直接忽略 `if` 代码块并继续执行下面的代码。
@ -35,27 +25,19 @@ fn main() {
尝试运行代码,应该能看到如下输出:
```console
$ cargo run
Compiling branches v0.1.0 (file:///projects/branches)
Finished dev [unoptimized + debuginfo] target(s) in 0.31s
Running `target/debug/branches`
condition was true
{{#include ../listings/ch03-common-programming-concepts/no-listing-26-if-true/output.txt}}
```
尝试改变 `number` 的值使条件为 `false` 时看看会发生什么:
```rust,ignore
let number = 7;
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-27-if-false/src/main.rs:here}}
```
再次运行程序并查看输出:
```console
$ cargo run
Compiling branches v0.1.0 (file:///projects/branches)
Finished dev [unoptimized + debuginfo] target(s) in 0.31s
Running `target/debug/branches`
condition was false
{{#include ../listings/ch03-common-programming-concepts/no-listing-27-if-false/output.txt}}
```
另外值得注意的是代码中的条件 **必须** 是 `bool` 值。如果条件不是 `bool` 值,我们将得到一个错误。例如,尝试运行以下代码:
@ -63,32 +45,13 @@ condition was false
<span class="filename">文件名: src/main.rs</span>
```rust,ignore,does_not_compile
fn main() {
let number = 3;
if number {
println!("number was three");
}
}
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-28-if-condition-must-be-bool/src/main.rs}}
```
这里 `if` 条件的值是 `3`Rust 抛出了一个错误:
```console
$ cargo run
Compiling branches v0.1.0 (file:///projects/branches)
error[E0308]: mismatched types
--> src/main.rs:4:8
|
4 | if number {
| ^^^^^^ expected `bool`, found integer
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.
error: could not compile `branches`
To learn more, run the command again with --verbose.
{{#include ../listings/ch03-common-programming-concepts/no-listing-28-if-condition-must-be-bool/output.txt}}
```
这个错误表明 Rust 期望一个 `bool` 却得到了一个整数。不像 Ruby 或 JavaScript 这样的语言Rust 并不会尝试自动地将非布尔值转换为布尔值。必须总是显式地使用布尔值作为 `if` 的条件。例如,如果想要 `if` 代码块只在一个数字不等于 `0` 时执行,可以把 `if` 表达式修改成下面这样:
@ -96,13 +59,7 @@ To learn more, run the command again with --verbose.
<span class="filename">文件名: src/main.rs</span>
```rust
fn main() {
let number = 3;
if number != 0 {
println!("number was something other than zero");
}
}
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-29-if-not-equal-0/src/main.rs}}
```
运行代码会打印出 `number was something other than zero`
@ -114,29 +71,13 @@ fn main() {
<span class="filename">文件名: src/main.rs</span>
```rust
fn main() {
let number = 6;
if number % 4 == 0 {
println!("number is divisible by 4");
} else if number % 3 == 0 {
println!("number is divisible by 3");
} else if number % 2 == 0 {
println!("number is divisible by 2");
} else {
println!("number is not divisible by 4, 3, or 2");
}
}
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-30-else-if/src/main.rs}}
```
这个程序有四个可能的执行路径。运行后应该能看到如下输出:
```console
$ cargo run
Compiling branches v0.1.0 (file:///projects/branches)
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
Running `target/debug/branches`
number is divisible by 3
{{#include ../listings/ch03-common-programming-concepts/no-listing-30-else-if/output.txt}}
```
当执行这个程序时,它按顺序检查每个 `if` 表达式并执行第一个条件为真的代码块。注意即使 6 可以被 2 整除,也不会输出 `number is divisible by 2`,更不会输出 `else` 块中的 `number is not divisible by 4, 3, or 2`。原因是 Rust 只会执行第一个条件为真的代码块,并且一旦它找到一个以后,甚至都不会检查剩下的条件了。
@ -150,16 +91,7 @@ number is divisible by 3
<span class="filename">文件名: src/main.rs</span>
```rust
fn main() {
let condition = true;
let number = if condition {
5
} else {
6
};
println!("The value of number is: {}", number);
}
{{#rustdoc_include ../listings/ch03-common-programming-concepts/listing-03-02/src/main.rs}}
```
<span class="caption">示例 3-2`if` 表达式的返回值赋给一个变量</span>
@ -167,11 +99,7 @@ fn main() {
`number` 变量将会绑定到表示 `if` 表达式结果的值上。运行这段代码看看会出现什么:
```console
$ cargo run
Compiling branches v0.1.0 (file:///projects/branches)
Finished dev [unoptimized + debuginfo] target(s) in 0.30s
Running `target/debug/branches`
The value of number is: 5
{{#include ../listings/ch03-common-programming-concepts/listing-03-02/output.txt}}
```
记住,代码块的值是其最后一个表达式的值,而数字本身就是一个表达式。在这个例子中,整个 `if` 表达式的值取决于哪个代码块被执行。这意味着 `if` 的每个分支的可能的返回值都必须是相同类型;在示例 3-2 中,`if` 分支和 `else` 分支的结果都是 `i32` 整型。如果它们的类型不匹配,如下面这个例子,则会出现一个错误:
@ -179,41 +107,16 @@ The value of number is: 5
<span class="filename">文件名: src/main.rs</span>
```rust,ignore,does_not_compile
fn main() {
let condition = true;
let number = if condition {
5
} else {
"six"
};
println!("The value of number is: {}", number);
}
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-31-arms-must-return-same-type/src/main.rs}}
```
当编译这段代码时,会得到一个错误。`if` 和 `else` 分支的值类型是不相容的,同时 Rust 也准确地指出在程序中的何处发现的这个问题:
```console
$ cargo run
Compiling branches v0.1.0 (file:///projects/branches)
error[E0308]: `if` and `else` have incompatible types
--> src/main.rs:4:44
|
4 | let number = if condition { 5 } else { "six" };
| - ^^^^^ expected integer, found `&str`
| |
| expected because of this
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.
error: could not compile `branches`
To learn more, run the command again with --verbose.
{{#include ../listings/ch03-common-programming-concepts/no-listing-31-arms-must-return-same-type/output.txt}}
```
`if` 代码块中的表达式返回一个整数,而 `else` 代码块中的表达式返回一个字符串。这不可行因为变量必须只有一个类型。Rust 需要在编译时就确切的知道 `number` 变量的类型,这样它就可以在编译时验证在每处使用的 `number` 变量的类型是有效的。如果`number`的类型仅在运行时确定则Rust无法做到这一点且编译器必须跟踪每一个变量的多种假设类型那么它就会变得更加复杂对代码的保证也会减少。
`if` 代码块中的表达式返回一个整数,而 `else` 代码块中的表达式返回一个字符串。这不可行因为变量必须只有一个类型。Rust 需要在编译时就确切的知道 `number` 变量的类型,这样它就可以在编译时验证在每处使用的 `number` 变量的类型是有效的。如果`number`的类型仅在运行时确定,则 Rust 无法做到这一点;且编译器必须跟踪每一个变量的多种假设类型,那么它就会变得更加复杂,对代码的保证也会减少。
### 使用循环重复执行
@ -230,11 +133,7 @@ Rust 有三种循环:`loop`、`while` 和 `for`。我们每一个都试试。
<span class="filename">文件名: src/main.rs</span>
```rust,ignore
fn main() {
loop {
println!("again!");
}
}
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-32-loop/src/main.rs}}
```
当运行这个程序时,我们会看到连续的反复打印 `again!`,直到我们手动停止程序。大部分终端都支持一个快捷键,<span class="keystroke">ctrl-c</span>,来终止一个陷入无限循环的程序。尝试一下:
@ -260,65 +159,21 @@ again!
如果存在嵌套循环,`break` 和 `continue` 应用于此时最内层的循环。你可以选择在一个循环上指定一个 **循环标签***loop label*),然后将标签与 `break``continue` 一起使用,使这些关键字应用于已标记的循环而不是最内层的循环。下面是一个包含两个嵌套循环的示例
```rust
fn main() {
let mut count = 0;
'counting_up: loop {
println!("count = {}", count);
let mut remaining = 10;
loop {
println!("remaining = {}", remaining);
if remaining == 9 {
break;
}
if count == 2 {
break 'counting_up;
}
remaining -= 1;
}
count += 1;
}
println!("End count = {}", count);
}
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-32-5-loop-labels/src/main.rs}}
```
外层循环有一个标签 `counting_up`,它将从 0 数到 2。没有标签的内部循环从 10 向下数到 9。第一个没有指定标签的 `break` 将只退出内层循环。`break 'counting_up;` 语句将退出外层循环。这个代码打印:
```console
$ cargo run
Compiling loops v0.1.0 (file:///projects/loops)
Finished dev [unoptimized + debuginfo] target(s) in 0.58s
Running `target/debug/loops`
count = 0
remaining = 10
remaining = 9
count = 1
remaining = 10
remaining = 9
count = 2
remaining = 10
End count = 2
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-32-5-loop-labels/output.txt}}
```
#### 从循环返回
#### 从循环返回值
`loop` 的一个用例是重试可能会失败的操作,比如检查线程是否完成了任务。然而你可能会需要将操作的结果传递给其它的代码。如果将返回值加入你用来停止循环的 `break` 表达式,它会被停止的循环返回:
```rust
fn main() {
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2;
}
};
println!("The result is {}", result);
}
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-33-return-value-from-loop/src/main.rs}}
```
在循环之前,我们声明了一个名为 `counter` 的变量并初始化为 `0`。接着声明了一个名为 `result` 来存放循环的返回值。在循环的每一次迭代中,我们将 `counter` 变量加 `1`,接着检查计数是否等于 `10`。当相等时,使用 `break` 关键字返回值 `counter * 2`。循环之后,我们通过分号结束赋值给 `result` 的语句。最后打印出 `result` 的值,也就是 20。
@ -332,17 +187,7 @@ fn main() {
<span class="filename">文件名: src/main.rs</span>
```rust
fn main() {
let mut number = 3;
while number != 0 {
println!("{}!", number);
number = number - 1;
}
println!("LIFTOFF!!!");
}
{{#rustdoc_include ../listings/ch03-common-programming-concepts/listing-03-03/src/main.rs}}
```
<span class="caption">示例 3-3: 当条件为真时,使用 `while` 循环运行代码</span>
@ -356,16 +201,7 @@ fn main() {
<span class="filename">文件名: src/main.rs</span>
```rust
fn main() {
let a = [10, 20, 30, 40, 50];
let mut index = 0;
while index < 5 {
println!("the value is: {}", a[index]);
index = index + 1;
}
}
{{#rustdoc_include ../listings/ch03-common-programming-concepts/listing-03-04/src/main.rs}}
```
<span class="caption">示例 3-4使用 `while` 循环遍历集合中的元素</span>
@ -373,33 +209,19 @@ fn main() {
这里,代码对数组中的元素进行计数。它从索引 `0` 开始,并接着循环直到遇到数组的最后一个索引(这时,`index < 5` 不再为真)。运行这段代码会打印出数组中的每一个元素:
```console
$ cargo run
Compiling loops v0.1.0 (file:///projects/loops)
Finished dev [unoptimized + debuginfo] target(s) in 0.32s
Running `target/debug/loops`
the value is: 10
the value is: 20
the value is: 30
the value is: 40
the value is: 50
{{#include ../listings/ch03-common-programming-concepts/listing-03-04/output.txt}}
```
数组中的所有五个元素都如期被打印出来。尽管 `index` 在某一时刻会到达值 `5`,不过循环在其尝试从数组获取第六个值(会越界)之前就停止了。
但这个过程很容易出错;如果索引长度或测试条件不正确会导致程序 panic。这也使程序更慢因为编译器增加了运行时代码来对每次循环进行条件检查以确定在循环的每次迭代中索引是否在数组的边界内。
但这个过程很容易出错;如果索引长度或测试条件不正确会导致程序 panic。例如如果将 `a` 数组的定义改为包含 4 个元素而忘记了更新条件 `while index < 4`,则代码会 panic。这也使程序更慢因为编译器增加了运行时代码来对每次循环进行条件检查以确定在循环的每次迭代中索引是否在数组的边界内。
作为更简洁的替代方案,可以使用 `for` 循环来对一个集合的每个元素执行一些代码。`for` 循环看起来如示例 3-5 所示:
<span class="filename">文件名: src/main.rs</span>
```rust
fn main() {
let a = [10, 20, 30, 40, 50];
for element in a.iter() {
println!("the value is: {}", element);
}
}
{{#rustdoc_include ../listings/ch03-common-programming-concepts/listing-03-05/src/main.rs}}
```
<span class="caption">示例 3-5使用 `for` 循环遍历集合中的元素</span>
@ -415,12 +237,7 @@ fn main() {
<span class="filename">文件名: src/main.rs</span>
```rust
fn main() {
for number in (1..4).rev() {
println!("{}!", number);
}
println!("LIFTOFF!!!");
}
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-34-for-range/src/main.rs}}
```
这段代码看起来更帅气不是吗?

@ -0,0 +1,9 @@
span.caption {
font-size: .8em;
font-weight: 600;
}
span.caption code {
font-size: 0.875em;
font-weight: 400;
}
Loading…
Cancel
Save