mirror of
https://github.com/KaiserY/trpl-zh-cn
synced 2024-11-09 00:43:59 +08:00
check ch03-05
This commit is contained in:
parent
2cbb16c296
commit
9eb6f98334
@ -2,19 +2,19 @@
|
||||
|
||||
> [ch03-02-data-types.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch03-02-data-types.md)
|
||||
> <br>
|
||||
> commit fe4833a8ef2853c55424e7747a4ef8dd64c35b32
|
||||
> commit 1d4f34661d4b13ecda4885d496e1b84cdbcbed31
|
||||
|
||||
在 Rust 中,任何值都属于一种明确的**类型**(*type*),声明它被指定了何种数据,以便明确其处理方式。我们将分两部分探讨一些内建类型:标量(scalar)和复合(compound)。
|
||||
在 Rust 中,任何值都属于一种明确的 **类型**(*type*),这告诉了 Rust 它被指定了何种数据,以便明确其处理方式。我们将分两部分探讨一些内建类型:标量(scalar)和复合(compound)。
|
||||
|
||||
Rust 是**静态类型**(*statically typed*)语言,也就是说在编译时就需要知道所有变量的类型,这一认知将贯穿整个章节,请在头脑中明确。通过值的形式及其使用方式,编译器通常可以推断出我们想要用的类型。多种类型均有可能时,比如第二章中使用 `parse` 将 `String` 转换为数字,必须增加类型注解,像这样:
|
||||
Rust 是 **静态类型**(*statically typed*)语言,也就是说在编译时就必须知道所有变量的类型,这一认知将贯穿整个章节,请在头脑中明确。通过值的形式及其使用方式,编译器通常可以推断出我们想要用的类型。多种类型均有可能时,比如第二章中使用 `parse` 将 `String` 转换为数字时,必须增加类型注解,像这样:
|
||||
|
||||
```rust
|
||||
let guess: u32 = "42".parse().expect("Not a number!");
|
||||
```
|
||||
|
||||
如果不添加类型注解,Rust 会显示如下错误。这说明编译器需要更多信息,来了解我们想要的类型:
|
||||
这里如果不添加类型注解,Rust 会显示如下错误,这说明编译器需要更多信息,来了解我们想要的类型:
|
||||
|
||||
```
|
||||
```text
|
||||
error[E0282]: unable to infer enough type information about `_`
|
||||
--> src/main.rs:2:9
|
||||
|
|
||||
@ -28,15 +28,15 @@ error[E0282]: unable to infer enough type information about `_`
|
||||
|
||||
### 标量类型
|
||||
|
||||
**标量**类型代表一个单独的值。Rust 有四种基本的标量类型:整型、浮点型、布尔类型和字符类型。你可能在其他语言中见过他们,不过让我们深入了解他们在 Rust 中时如何工作的。
|
||||
**标量**(*scalar*)类型代表一个单独的值。Rust 有四种基本的标量类型:整型、浮点型、布尔类型和字符类型。你可能在其他语言中见过他们,不过让我们深入了解他们在 Rust 中时如何工作的。
|
||||
|
||||
#### 整型
|
||||
|
||||
**整数**是一个没有小数部分的数字。我们在这一章的前面使用过 `i32` 类型。该类型声明指示,i32 关联的值应该是一个占据32比特位的有符号整数(因为这个`i`,与`u`代表的无符号相对)。表格 3-1 展示了 Rust 内建的整数类型。每一种变体的有符号和无符号列(例如,*i32*)可以用来声明对应的整数值。
|
||||
**整数** 是一个没有小数部分的数字。我们在这一章的前面使用过 `i32` 类型。该类型声明指示,i32 关联的值应该是一个占据 32 比特位的有符号整数(因为这个`i`,与`u`代表的无符号相对)。表格 3-1 展示了 Rust 内建的整数类型。每一种变体的有符号和无符号列(例如,*i32*)可以用来声明对应的整数值。
|
||||
|
||||
<span class="caption">Table 3-1: Integer Types in Rust</span>
|
||||
<span class="caption">表格 3-1: Rust 中的整型</span>
|
||||
|
||||
| Length | Signed | Unsigned |
|
||||
| 长度 | 有符号 | 无符号 |
|
||||
|--------|--------|----------|
|
||||
| 8-bit | i8 | u8 |
|
||||
| 16-bit | i16 | u16 |
|
||||
@ -50,11 +50,11 @@ error[E0282]: unable to infer enough type information about `_`
|
||||
|
||||
另外,`isize` 和 `usize` 类型依赖运行程序的计算机架构:64 位架构上他们是 64 位的, 32 位架构上他们是 32 位的。
|
||||
|
||||
可以使用表格 3-2 中的任何一种形式编写数字字面值。除字节以外的其它字面值允许使用类型后缀,例如 `57u8`,允许使用 `_` 做为分隔符以方便读数,例如 `1_000` (分隔符的数量与位置并不影响实际的数字)。
|
||||
可以使用表格 3-2 中的任何一种形式编写数字字面值。注意除字节以外的其它字面值允许使用类型后缀,例如 `57u8`,允许使用 `_` 做为分隔符以方便读数。
|
||||
|
||||
<span class="caption">Table 3-2: Integer Literals in Rust</span>
|
||||
<span class="caption">表格 3-2: RUst 中的整型字面值</span>
|
||||
|
||||
| Number literals | Example |
|
||||
| 数字字面值 | 例子 |
|
||||
|------------------|---------------|
|
||||
| Decimal | `98_222` |
|
||||
| Hex | `0xff` |
|
||||
@ -62,15 +62,15 @@ error[E0282]: unable to infer enough type information about `_`
|
||||
| Binary | `0b1111_0000` |
|
||||
| Byte (`u8` only) | `b'A'` |
|
||||
|
||||
那么该使用哪种类型的数字呢?如果拿不定主意,Rust 的默认类型通常就很好,数字类型默认是 `i32`:它通常是最快的,甚至在 64 位系统上也是。`isize` 或 `usize` 的主要作为集合的索引。
|
||||
那么该使用哪种类型的数字呢?如果拿不定主意,Rust 的默认类型通常就很好,数字类型默认是 `i32`:它通常是最快的,甚至在 64 位系统上也是。`isize` 或 `usize` 主要作为集合的索引。
|
||||
|
||||
#### 浮点型
|
||||
|
||||
Rust 同样有两个主要的**浮点数**类型,`f32` 和 `f64`,它们是带小数点的数字,分别占 32 位和 64 位比特。默认类型是 `f64`,因为它与 `f32` 速度差不多,然而精度更高。在 32 位系统上也能够使用 `f64`,不过比使用 `f32` 要慢。多数情况下,以潜在的性能损耗换取更高的精度是合理的;如果觉得浮点数的大小是个麻烦,你应该以性能测试作为决策依据。
|
||||
Rust 同样有两个主要的 **浮点数**(*floating-point numbers*)类型,`f32` 和 `f64`,它们是带小数点的数字,分别占 32 位和 64 位比特。默认类型是 `f64`,因为它与 `f32` 速度差不多,然而精度更高。在 32 位系统上也能够使用 `f64`,不过比使用 `f32` 要慢。多数情况下,一开始以潜在的性能损耗换取更高的精度是合理的;如果觉得浮点数的大小是个麻烦,你应该以性能测试作为决策依据。
|
||||
|
||||
这是一个展示浮点数的实例:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
@ -80,13 +80,13 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
浮点数采用 IEEE-754 标准表示。`f32`是单精度浮点数,`f64`是双精度浮点数。
|
||||
浮点数采用 IEEE-754 标准表示。`f32` 是单精度浮点数,`f64` 是双精度浮点数。
|
||||
|
||||
#### 数字运算符
|
||||
|
||||
Rust 支持所有数字类型常见的基本数学运算操作:加法、减法、乘法、除法以及余数。如下代码展示了如何使用一个`let`语句来使用他们:
|
||||
Rust 支持所有数字类型常见的基本数学运算操作:加法、减法、乘法、除法以及余数。如下代码展示了如何使用一个 `let` 语句来使用他们:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
@ -111,10 +111,9 @@ fn main() {
|
||||
|
||||
#### 布尔型
|
||||
|
||||
正如其他大部分编程语言一样,Rust 中的布尔类型有两个可能的值:`true`和`false`。Rust 中的布尔类型使用`bool`表示。例如:
|
||||
正如其他大部分编程语言一样,Rust 中的布尔类型有两个可能的值:`true` 和 `false`。Rust 中的布尔类型使用 `bool` 表示。例如:
|
||||
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
@ -124,13 +123,13 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
使用布尔值的主要场景是条件表达式,例如`if`。在“控制流”(“Control Flow”)部分将讲到`if`表达式在 Rust 中如何工作。
|
||||
使用布尔值的主要场景是条件表达式,例如 `if` 表达式。在 “控制流”(“Control Flow”)部分将讲到`if`表达式在 Rust 中如何工作。
|
||||
|
||||
#### 字符类型
|
||||
|
||||
目前为止只使用到了数字,不过 Rust 也支持字符。Rust 的`char`类型是大部分语言中基本字母字符类型,如下代码展示了如何使用它:
|
||||
目前为止只使用到了数字,不过 Rust 也支持字符。Rust 的 `char` 类型是大部分语言中基本字母字符类型,如下代码展示了如何使用它:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
@ -140,11 +139,11 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
Rust 的`char`类型代表了一个 Unicode 变量值(Unicode Scalar Value),这意味着它可以比 ASCII 表示更多内容。拼音字母(Accented letters),中文/日文/汉语等象形文字,emoji(絵文字)以及零长度的空白字符对于 Rust `char`类型都是有效的。Unicode 标量值包含从 `U+0000` 到 `U+D7FF` 和 `U+E000` 到 `U+10FFFF` 之间的值。不过,“字符”并不是一个 Unicode 中的概念,所以人直觉上的“字符”可能与 Rust 中的`char`并不符合。第八章的“字符串”部分将详细讨论这个主题。
|
||||
Rust 的 `char` 类型代表了一个 Unicode 标量值(Unicode Scalar Value),这意味着它可以比 ASCII 表示更多内容。拼音字母(Accented letters),中文/日文/汉语等象形文字,emoji(絵文字)以及零长度的空白字符对于 Rust `char`类型都是有效的。Unicode 标量值包含从 `U+0000` 到 `U+D7FF` 和 `U+E000` 到 `U+10FFFF` 之间的值。不过,“字符” 并不是一个 Unicode 中的概念,所以人直觉上的 “字符” 可能与 Rust 中的 `char` 并不符合。第八章的 “字符串” 部分将详细讨论这个主题。
|
||||
|
||||
### 复合类型
|
||||
|
||||
**复合类型**可以将多个其他类型的值组合成一个类型。Rust 有两个原生的复合类型:元组(tuple)和数组(array)。
|
||||
**复合类型**(*Compound types*)可以将多个其他类型的值组合成一个类型。Rust 有两个原生的复合类型:元组(tuple)和数组(array)。
|
||||
|
||||
#### 将值组合进元组
|
||||
|
||||
@ -152,7 +151,7 @@ Rust 的`char`类型代表了一个 Unicode 变量值(Unicode Scalar Value)
|
||||
|
||||
我们使用一个括号中的逗号分隔的值列表来创建一个元组。元组中的每一个位置都有一个类型,而且这些不同值的类型也不必是相同的。这个例子中使用了额外的可选类型注解:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
@ -160,9 +159,9 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
`tup`变量绑定了整个元组,因为元组被认为是一个单独的复合元素。为了从元组中获取单个的值,可以使用模式匹配(pattern matching)来解构(destructure )元组,像这样:
|
||||
`tup` 变量绑定了整个元组,因为元组被认为是一个单独的复合元素。为了从元组中获取单个的值,可以使用模式匹配(pattern matching)来解构(destructure)元组,像这样:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
@ -174,11 +173,11 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
程序首先创建了一个元组并绑定到`tup`变量上。接着使用了`let`和一个模式将`tup`分成了三个不同的变量,`x`、`y`和`z`。这叫做*解构*(*destructuring*),因为它将一个元组拆成了三个部分。最后,程序打印出了`y`的值,也就是`6.4`。
|
||||
程序首先创建了一个元组并绑定到 `tup` 变量上。接着使用了 `let` 和一个模式将 `tup` 分成了三个不同的变量,`x`、`y` 和 `z`。这叫做 *解构*(*destructuring*),因为它将一个元组拆成了三个部分。最后,程序打印出了 `y` 的值,也就是 `6.4`。
|
||||
|
||||
除了使用模式匹配解构之外,也可以使用点号(`.`)后跟值的索引来直接访问他们。例如:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
@ -196,11 +195,11 @@ fn main() {
|
||||
|
||||
#### 数组
|
||||
|
||||
另一个获取一个多个值集合的方式是**数组**(*array*)。与元组不同,数组中的每个元素的类型必须相同。Rust 中的数组与一些其他语言中的数组不同,因为 Rust 中的数组是固定长度的:一旦声明,他们的长度不能增长或缩小。
|
||||
另一个获取一个多个值集合的方式是 **数组**(*array*)。与元组不同,数组中的每个元素的类型必须相同。Rust 中的数组与一些其他语言中的数组不同,因为 Rust 中的数组是固定长度的:一旦声明,他们的长度不能增长或缩小。
|
||||
|
||||
Rust 中数组的值位于中括号中的逗号分隔的列表中:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
@ -208,7 +207,7 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
数组在想要在栈(stack)而不是在堆(heap)上为数据分配空间时十分有用(第四章将讨论栈与堆的更多内容),或者是想要确保总是有固定数量的元素时。虽然它并不如 vector 类型那么灵活。vector 类型是标准库提供的一个**允许**增长和缩小长度的类似数组的集合类型。当不确定是应该使用数组还是 vector 的时候,你可能应该使用 vector:第八章会详细讨论 vector。
|
||||
数组在需要在栈(stack)而不是在堆(heap)上为数据分配空间时(第四章将讨论栈与堆的更多内容),或者是想要确保总是有固定数量的元素时十分有用。虽然它并不如 vector 类型那么灵活。vector 类型是标准库提供的一个 **允许** 增长和缩小长度的类似数组的集合类型。当不确定是应该使用数组还是 vector 的时候,你可能应该使用 vector:第八章会详细讨论 vector。
|
||||
|
||||
一个你可能想要使用数组而不是 vector 的例子是当程序需要知道一年中月份的名字时。程序不大可能回去增加或减少月份,这时你可以使用数组因为我们知道它总是含有 12 个元素:
|
||||
|
||||
@ -221,7 +220,7 @@ let months = ["January", "February", "March", "April", "May", "June", "July",
|
||||
|
||||
数组是一整块分配在栈上的内存。可以使用索引来访问数组的元素,像这样:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
@ -232,36 +231,36 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
在这个例子中,叫做`first`的变量的值是`1`,因为它是数组索引`[0]`的值。`second`将会是数组索引`[1]`的值`2`。
|
||||
在这个例子中,叫做 `first` 的变量的值是 `1`,因为它是数组索引 `[0]` 的值。`second` 将会是数组索引 `[1]` 的值 `2`。
|
||||
|
||||
##### 无效的数组元素访问
|
||||
|
||||
如果我们访问数组结尾之后的元素会发生什么呢?比如我们将上面的例子改为如下:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
fn main() {
|
||||
let a = [1, 2, 3, 4, 5];
|
||||
let index = 10;
|
||||
|
||||
let element = a[10];
|
||||
let element = a[index];
|
||||
|
||||
println!("The value of element is: {}", element);
|
||||
}
|
||||
```
|
||||
|
||||
使用`cargo run`运行代码后会产生如下结果:
|
||||
使用 `cargo run` 运行代码后会产生如下结果:
|
||||
|
||||
```
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling arrays v0.1.0 (file:///projects/arrays)
|
||||
Running `target/debug/arrays`
|
||||
thread '<main>' panicked at 'index out of bounds: the len is 5 but the index is
|
||||
10', src/main.rs:4
|
||||
10', src/main.rs:6
|
||||
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
||||
error: Process didn't exit successfully: `target/debug/arrays` (exit code: 101)
|
||||
```
|
||||
|
||||
编译并没有产生任何错误,不过程序会导致一个**运行时**(*runtime*)错误并且不会成功退出。当尝试用索引访问一个元素时,Rust 会检查指定的索引是否小于数组的长度。如果索引超出了数组长度,Rust 会*panic*,这是 Rust 中的术语,它用于程序因为错误而退出的情况。
|
||||
编译并没有产生任何错误,不过程序会导致一个 **运行时**(*runtime*)错误并且不会成功退出。当尝试用索引访问一个元素时,Rust 会检查指定的索引是否小于数组的长度。如果索引超出了数组长度,Rust 会 *panic*,这是 Rust 中的术语,它用于程序因为错误而退出的情况。
|
||||
|
||||
这是第一个在实战中遇到的 Rust 安全原则的例子。在很多底层语言中,并没有进行这类检查,这样当提供了一个不正确的索引时,就会访问无效的内存。Rust 通过立即退出而不是允许内存访问并继续执行来使你免受这类错误困扰。第九章会讨论更多 Rust 的错误处理。
|
||||
|
@ -2,13 +2,13 @@
|
||||
|
||||
> [ch03-03-how-functions-work.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch03-03-how-functions-work.md)
|
||||
> <br>
|
||||
> commit 04aa3a45eb72855b34213703718f50a12a3eeec8
|
||||
> commit d06a6a181fd61704cbf7feb55bc61d518c6469f9
|
||||
|
||||
函数在 Rust 代码中应用广泛。你已经见过一个语言中最重要的函数:`main`函数,它时很多程序的入口点。你也见过了`fn`关键字,它用来声明新函数。
|
||||
函数在 Rust 代码中应用广泛。你已经见过一个语言中最重要的函数:`main` 函数,它是很多程序的入口点。你也见过了 `fn` 关键字,它用来声明新函数。
|
||||
|
||||
Rust 代码使用 *snake case* 作为函数和变量名称的规范风格。在 snake case 中,所有字母都是小写并使用下划线分隔单词。这里是一个函数定义程序的例子:
|
||||
Rust 代码使用 *snake case* 作为函数和变量名称的规范风格。在 snake case 中,所有字母都是小写并使用下划线分隔单词。这里是一个包含函数定义的程序的例子:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
@ -22,13 +22,13 @@ fn another_function() {
|
||||
}
|
||||
```
|
||||
|
||||
Rust 中的函数定义以`fn`开始并在函数名后跟一对括号。大括号告诉编译器哪里是函数体的开始和结尾。
|
||||
Rust 中的函数定义以 `fn` 开始并在函数名后跟一对括号。大括号告诉编译器哪里是函数体的开始和结尾。
|
||||
|
||||
可以使用定义过的函数名后跟括号来调用任意函数。因为`another_function`已经在程序中定义过了,它可以在`main`函数中被调用。注意,源码中`another_function`在`main`函数*之后*被定义;也可以在其之前定义。Rust 不关心函数定义于何处,只要他们被定义了。
|
||||
可以使用定义过的函数名后跟括号来调用任意函数。因为 `another_function` 已经在程序中定义过了,它可以在 `main` 函数中被调用。注意,源码中 `another_function` 在 `main` 函数 **之后** 被定义;也可以在其之前定义。Rust 不关心函数定义于何处,只要他们被定义了。
|
||||
|
||||
让我们开始一个叫做 *functions* 的新二进制项目来进一步探索函数。将上面的`another_function`例子写入 *src/main.rs* 中并运行。你应该会看到如下输出:
|
||||
让我们开始一个叫做 *functions* 的新二进制项目来进一步探索函数。将上面的 `another_function` 例子写入 *src/main.rs* 中并运行。你应该会看到如下输出:
|
||||
|
||||
```
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling functions v0.1.0 (file:///projects/functions)
|
||||
Running `target/debug/functions`
|
||||
@ -36,16 +36,15 @@ Hello, world!
|
||||
Another function.
|
||||
```
|
||||
|
||||
代码在`main`函数中按照他们出现的顺序被执行。首先,打印“Hello, world!”信息,接着`another_function`被调用并打印它的信息。
|
||||
代码在 `main` 函数中按照他们出现的顺序被执行。首先,打印 “Hello, world!” 信息,接着 `another_function` 被调用并打印它的信息。
|
||||
|
||||
### 函数参数
|
||||
|
||||
函数也可以被定义为拥有**参数**(*parameters*),他们是作为函数签名一部分的特殊变量。当函数拥有参数,可以为这些参数提供具体的值。技术上讲,这些具体值被称为参数( *arguments*),不过通常的习惯是倾向于在函数定义中的变量和调用函数时传递的具体值都可以用“parameter”和“argument”而不加区别。
|
||||
函数也可以被定义为拥有 **参数**(*parameters*),他们是作为函数签名一部分的特殊变量。当函数拥有参数时,可以为这些参数提供具体的值。技术上讲,这些具体值被称为参数( *arguments*),不过通常的习惯是倾向于在函数定义中的变量和调用函数时传递的具体值都可以用 “parameter” 和 “argument” 而不加区别。
|
||||
|
||||
如下被重写的`another_function`版本展示了 Rust 中参数是什么样的:
|
||||
如下被重写的 `another_function` 版本展示了 Rust 中参数是什么样的:
|
||||
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
@ -59,20 +58,20 @@ fn another_function(x: i32) {
|
||||
|
||||
尝试运行程序,将会得到如下输出:
|
||||
|
||||
```sh
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling functions v0.1.0 (file:///projects/functions)
|
||||
Running `target/debug/functions`
|
||||
The value of x is: 5
|
||||
```
|
||||
|
||||
`another_function`的声明有一个叫做`x`的参数。`x`的类型被指定为`i32`。当`5`被传递给`another_function`时,`println!`宏将`5`放入格式化字符串中大括号的位置。
|
||||
`another_function` 的声明有一个叫做 `x` 的参数。`x` 的类型被指定为 `i32`。当 `5` 被传递给 `another_function` 时,`println!` 宏将 `5` 放入格式化字符串中大括号的位置。
|
||||
|
||||
在函数签名中,**必须**声明每个参数的类型。这是 Rust 设计中一个经过慎重考虑的决定:要求在函数定义中提供类型注解意味着编译器再也不需要在别的地方要求你注明类型就能知道你的意图。
|
||||
在函数签名中,**必须** 声明每个参数的类型。这是 Rust 设计中一个经过慎重考虑的决定:要求在函数定义中提供类型注解意味着编译器再也不需要在别的地方要求你注明类型就能知道你的意图。
|
||||
|
||||
当一个函数有多个参数时,使用逗号隔开他们,像这样:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
@ -85,11 +84,11 @@ fn another_function(x: i32, y: i32) {
|
||||
}
|
||||
```
|
||||
|
||||
这个例子创建了一个有两个参数的函数,都是`i32`类型的。函数打印出了这两个参数的值。注意函数参数并不一定都是相同类型的,这个例子中他们只是碰巧相同罢了。
|
||||
这个例子创建了一个有两个参数的函数,都是 `i32` 类型的。函数打印出了这两个参数的值。注意函数参数并不一定都是相同类型的,这个例子中他们只是碰巧相同罢了。
|
||||
|
||||
尝试运行代码。使用上面的例子替换当前 *function* 项目的 *src/main.rs* 文件,并`cargo run`运行它:
|
||||
尝试运行代码。使用上面的例子替换当前 *function* 项目的 *src/main.rs* 文件,并 `cargo run` 运行它:
|
||||
|
||||
```sh
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling functions v0.1.0 (file:///projects/functions)
|
||||
Running `target/debug/functions`
|
||||
@ -97,7 +96,7 @@ The value of x is: 5
|
||||
The value of y is: 6
|
||||
```
|
||||
|
||||
因为我们使用`5`作为`x`的值和`6`作为`y`的值来调用函数,这两个字符串和他们的值并被打印出来。
|
||||
因为我们使用 `5` 作为 `x` 的值和 `6` 作为 `y` 的值来调用函数,这两个字符串和他们的值并被打印出来。
|
||||
|
||||
### 函数体
|
||||
|
||||
@ -107,9 +106,9 @@ The value of y is: 6
|
||||
|
||||
我们已经用过语句与表达式了。**语句**(*Statements*)是执行一些操作但不返回值的指令。表达式(*Expressions*)计算并产生一个值。让我们看看一些例子:
|
||||
|
||||
使用`let`关键字创建变量并绑定一个值是一个语句。在列表 3-3 中,`let y = 6;`是一个语句:
|
||||
使用 `let` 关键字创建变量并绑定一个值是一个语句。在列表 3-3 中,`let y = 6;` 是一个语句:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
@ -117,7 +116,7 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 3-3: A `main` function declaration containing one statement.</span>
|
||||
<span class="caption">列表 3-3:包含一个语句的 `main` 函数定义</span>
|
||||
|
||||
函数定义也是语句;上面整个例子本身就是一个语句。
|
||||
|
||||
@ -133,7 +132,7 @@ fn main() {
|
||||
|
||||
当运行这个程序,会得到如下错误:
|
||||
|
||||
```
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling functions v0.1.0 (file:///projects/functions)
|
||||
error: expected expression, found statement (`let`)
|
||||
@ -145,11 +144,11 @@ error: expected expression, found statement (`let`)
|
||||
= note: variable declaration using `let` is a statement
|
||||
```
|
||||
|
||||
`let y = 6`语句并不返回值,所以并没有`x`可以绑定的值。这与其他语言不同,例如 C 和 Ruby,他们的赋值语句返回所赋的值。在这些语言中,可以这么写`x = y = 6`这样`x`和`y`的值都是`6`;这在 Rust 中可不行。
|
||||
`let y = 6` 语句并不返回值,所以并没有 `x` 可以绑定的值。这与其他语言不同,例如 C 和 Ruby,他们的赋值语句返回所赋的值。在这些语言中,可以这么写 `x = y = 6` 这样 `x` 和 `y` 的值都是 `6`;这在 Rust 中可不行。
|
||||
|
||||
表达式计算出一些值,而且他们组成了其余大部分你将会编写的 Rust 代码。考虑一个简单的数学运算,比如`5 + 6`,这是一个表达式并计算出值`11`。表达式可以是语句的一部分:在列表 3-3 中有这个语句`let y = 6;`,`6`是一个表达式它计算出的值是`6`。函数调用是一个表达式。宏调用是一个表达式。我们用来创新建作用域的大括号(代码块),`{}`,也是一个表达式,例如:
|
||||
表达式计算出一些值,而且他们组成了其余大部分你将会编写的 Rust 代码。考虑一个简单的数学运算,比如 `5 + 6`,这是一个表达式并计算出值 `11`。表达式可以是语句的一部分:在列表 3-3 中有这个语句 `let y = 6;`,`6` 是一个表达式它计算出的值是 `6`。函数调用是一个表达式。宏调用是一个表达式。我们用来创新建作用域的大括号(代码块),`{}`,也是一个表达式,例如:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
@ -173,13 +172,13 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
这个代码块的值是`4`。这个值作为`let`语句的一部分被绑定到`y`上。注意结尾没有分号的那一行,与大部分我们见过的代码行不同。表达式并不包含结尾的分号。如果在表达式的结尾加上分号,他就变成了语句,这也就使其不返回一个值。在接下来的探索中记住函数和表达式都返回值就行了。
|
||||
这个代码块的值是 `4`。这个值作为 `let` 语句的一部分被绑定到 `y` 上。注意结尾没有分号的那一行,与大部分我们见过的代码行不同。表达式并不包含结尾的分号。如果在表达式的结尾加上分号,他就变成了语句,这也就使其不返回一个值。在接下来的探索中记住函数和表达式都返回值就行了。
|
||||
|
||||
### 函数的返回值
|
||||
|
||||
可以向调用它的代码返回值。并不对返回值命名,不过会在一个箭头(`->`)后声明它的类型。在 Rust 中,函数的返回值等同于函数体最后一个表达式的值。这是一个有返回值的函数的例子:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn five() -> i32 {
|
||||
@ -193,24 +192,24 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
在函数`five`中并没有函数调用、宏、甚至也没有`let`语句————只有数字`5`它自己。这在 Rust 中是一个完全有效的函数。注意函数的返回值类型也被指定了,就是`-> i32`。尝试运行代码;输出应该看起来像这样:
|
||||
在函数 `five` 中并没有函数调用、宏、甚至也没有 `let` 语句————只有数字 `5` 自身。这在 Rust 中是一个完全有效的函数。注意函数的返回值类型也被指定了,就是 `-> i32`。尝试运行代码;输出应该看起来像这样:
|
||||
|
||||
```
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling functions v0.1.0 (file:///projects/functions)
|
||||
Running `target/debug/functions`
|
||||
The value of x is: 5
|
||||
```
|
||||
|
||||
函数`five`的返回值是`5`,也就是为什么返回值类型是`i32`。让我们仔细检查一下这段代码。这有两个重要的部分:首先,`let x = five();`这一行表明我们使用函数的返回值来初始化了一个变量。因为函数`five`返回`5`,这一行与如下这行相同:
|
||||
函数 `five` 的返回值是 `5`,也就是为什么返回值类型是 `i32`。让我们仔细检查一下这段代码。这有两个重要的部分:首先,`let x = five();` 这一行表明我们使用函数的返回值来初始化了一个变量。因为函数 `five` 返回 `5`,这一行与如下这行相同:
|
||||
|
||||
```rust
|
||||
let x = 5;
|
||||
```
|
||||
|
||||
其次,函数`five`没有参数并定义了返回值类型,不过函数体只有单单一个`5`也没有分号,因为这是我们想要返回值的表达式。让我们看看另一个例子:
|
||||
其次,函数 `five` 没有参数并定义了返回值类型,不过函数体只有单单一个 `5` 也没有分号,因为这是我们想要返回值的表达式。让我们看看另一个例子:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
@ -224,9 +223,9 @@ fn plus_one(x: i32) -> i32 {
|
||||
}
|
||||
```
|
||||
|
||||
运行代码会打印出`The value of x is: 6`。如果在包含`x + 1`的那一行的结尾加上一个分号,把它从表达式变成语句后会怎样呢?
|
||||
运行代码会打印出 `The value of x is: 6`。如果在包含 `x + 1` 的那一行的结尾加上一个分号,把它从表达式变成语句后会怎样呢?
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
fn main() {
|
||||
@ -242,15 +241,15 @@ fn plus_one(x: i32) -> i32 {
|
||||
|
||||
运行代码会产生一个错误,如下:
|
||||
|
||||
```
|
||||
```text
|
||||
error[E0308]: mismatched types
|
||||
--> src/main.rs:7:28
|
||||
|
|
||||
7 | fn plus_one(x: i32) -> i32 {
|
||||
| ____________________________^ starting here...
|
||||
| ____________________________^
|
||||
8 | | x + 1;
|
||||
9 | | }
|
||||
| |_^ ...ending here: expected i32, found ()
|
||||
| |_^ expected i32, found ()
|
||||
|
|
||||
= note: expected type `i32`
|
||||
found type `()`
|
||||
@ -261,4 +260,4 @@ help: consider removing this semicolon:
|
||||
| ^
|
||||
```
|
||||
|
||||
主要的错误信息,“mismatched types,”(类型不匹配),揭示了代码的核心问题。函数`plus_one`的定义说明它要返回一个`i32`,不过语句并不返回一个值,这由那个空元组`()`表明。因此,这个函数返回了空元组`()`(译者注:原文说此函数没有返回任何值,可能有误),这与函数定义相矛盾并导致一个错误。在输出中,Rust 提供了一个可能会对修正问题有帮助的信息:它建议去掉分号,这会修复这个错误。
|
||||
主要的错误信息,“mismatched types,”(类型不匹配),揭示了代码的核心问题。函数`plus_one` 的定义说明它要返回一个 `i32`,不过语句并不返回一个值,这由那个空元组 `()` 表明。因此,这个函数返回了空元组 `()`,这与函数定义相矛盾并导致一个错误。在输出中,Rust 提供了一个可能会对修正问题有帮助的信息:它建议去掉分号,这会修复这个错误。
|
@ -2,9 +2,9 @@
|
||||
|
||||
> [ch03-04-comments.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch03-04-comments.md)
|
||||
> <br>
|
||||
> commit 4f2dc564851dc04b271a2260c834643dfd86c724
|
||||
> commit d06a6a181fd61704cbf7feb55bc61d518c6469f9
|
||||
|
||||
所有编程语言都力求使他们的代码易于理解,不过有时需要提供额外的解释。在这种情况下,程序员在源码中留下记录,或者**注释**(*comments*),编译器会忽略他们不过其他阅读代码的人可能会用得上。
|
||||
所有编程语言都力求使他们的代码易于理解,不过有时需要提供额外的解释。在这种情况下,程序员在源码中留下记录,或者 **注释**(*comments*),编译器会忽略他们不过其他阅读代码的人可能会用得上。
|
||||
|
||||
这是一个注释的例子:
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
|
||||
注释也可以在放在包含代码的行的末尾:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
@ -32,7 +32,7 @@ fn main() {
|
||||
|
||||
不过你会经常看到他们被以这种格式使用,也就是位于它所解释的代码行的上面一行:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
|
@ -2,17 +2,17 @@
|
||||
|
||||
> [ch03-05-control-flow.md](https://github.com/rust-lang/book/blob/master/src/ch03-05-control-flow.md)
|
||||
> <br>
|
||||
> commit 04aa3a45eb72855b34213703718f50a12a3eeec8
|
||||
> commit 2e269ff82193fd65df8a87c06561d74b51ac02f7
|
||||
|
||||
通过条件是不是真来决定是否某些代码,或者根据条件是否为真来重复运行一段代码是大部分编程语言的基本组成部分。Rust 代码中最常见的用来控制执行流的结构是`if`表达式和循环。
|
||||
通过条件是不是为真来决定是否执行某些代码,或者根据条件是否为真来重复运行一段代码是大部分编程语言的基本组成部分。Rust 代码中最常见的用来控制执行流的结构是 `if` 表达式和循环。
|
||||
|
||||
### `if`表达式
|
||||
### `if` 表达式
|
||||
|
||||
`if`表达式允许根据条件执行不同的代码分支。我们提供一个条件并表示“如果符合这个条件,运行这段代码。如果条件不满足,不运行这段代码。”
|
||||
`if` 表达式允许根据条件执行不同的代码分支。我们提供一个条件并表示 “如果符合这个条件,运行这段代码。如果条件不满足,不运行这段代码。”
|
||||
|
||||
在 *projects* 目录创建一个叫做 *branches* 的新项目来学习`if`表达式。在 *src/main.rs* 文件中,输入如下内容:
|
||||
在 *projects* 目录创建一个叫做 *branches* 的新项目来学习 `if` 表达式。在 *src/main.rs* 文件中,输入如下内容:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
@ -28,18 +28,18 @@ fn main() {
|
||||
|
||||
<!-- NEXT PARAGRAPH WRAPPED WEIRD INTENTIONALLY SEE #199 -->
|
||||
|
||||
所有`if`表达式以`if`关键字开头,它后跟一个条件。在这个例子中,条件检查`number`是否有一个小于 5 的值。在条件为真时希望执行的代码块位于紧跟条件之后的大括号中。`if`表达式中与条件关联的代码块有时被叫做 *arms*,就像第二章“比较猜测与秘密数字”部分中讨论到的`match`表达式中分支一样。也可以包含一个可选的`else`表达式,这里我们就这么做了,来提供一个在条件为假时应当执行的代码块。如果不提供`else`表达式并且条件为假时,程序会直接忽略`if`代码块并继续执行下面的代码。
|
||||
所有 `if` 表达式以 `if` 关键字开头,它后跟一个条件。在这个例子中,条件检查 `number` 是否有一个小于 5 的值。在条件为真时希望执行的代码块位于紧跟条件之后的大括号中。`if` 表达式中与条件关联的代码块有时被叫做 *arms*,就像第二章 “比较猜测与秘密数字” 部分中讨论到的 `match` 表达式中分支一样。也可以包含一个可选的 `else` 表达式,这里我们就这么做了,来提供一个在条件为假时应当执行的代码块。如果不提供 `else` 表达式并且条件为假时,程序会直接忽略 `if` 代码块并继续执行下面的代码。
|
||||
|
||||
尝试运行代码,应该能看到如下输出:
|
||||
|
||||
```
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling branches v0.1.0 (file:///projects/branches)
|
||||
Running `target/debug/branches`
|
||||
condition was true
|
||||
```
|
||||
|
||||
尝试改变`number`的值使条件为假时看看会发生什么:
|
||||
尝试改变 `number` 的值使条件为假时看看会发生什么:
|
||||
|
||||
```rust,ignore
|
||||
let number = 7;
|
||||
@ -47,16 +47,16 @@ let number = 7;
|
||||
|
||||
再次运行程序并查看输出:
|
||||
|
||||
```
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling branches v0.1.0 (file:///projects/branches)
|
||||
Running `target/debug/branches`
|
||||
condition was false
|
||||
```
|
||||
|
||||
另外值得注意的是代码中的条件**必须**是`bool`。如果像看看条件不是`bool`值时会发生什么,尝试运行如下代码:
|
||||
另外值得注意的是代码中的条件 **必须** 是 `bool`。如果像看看条件不是 `bool` 值时会发生什么,尝试运行如下代码:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
fn main() {
|
||||
@ -68,9 +68,9 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
这里`if`条件的值是`3`,Rust 抛出了一个错误:
|
||||
这里 `if` 条件的值是 `3`,Rust 抛出了一个错误:
|
||||
|
||||
```
|
||||
```text
|
||||
error[E0308]: mismatched types
|
||||
--> src/main.rs:4:8
|
||||
|
|
||||
@ -81,9 +81,9 @@ error[E0308]: mismatched types
|
||||
found type `{integer}`
|
||||
```
|
||||
|
||||
这个错误表明 Rust 期望一个`bool`不过却得到了一个整型。Rust 并不会尝试自动地将非布尔值转换为布尔值,不像例如 Ruby 和 JavaScript 这样的语言。必须总是显式地使用`boolean`作为`if`的条件。例如如果想要`if`代码块只在一个数字不等于`0`时执行,可以把`if`表达式修改为如下:
|
||||
这个错误表明 Rust 期望一个 `bool` 不过却得到了一个整型。Rust 并不会尝试自动地将非布尔值转换为布尔值,不像例如 Ruby 和 JavaScript 这样的语言。必须总是显式地使用 `boolean` 作为 `if` 的条件。例如如果想 要`if` 代码块只在一个数字不等于 `0` 时执行,可以把 `if` 表达式修改为如下:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
@ -95,13 +95,13 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
运行代码会打印出`number was something other than zero`。
|
||||
运行代码会打印出 `number was something other than zero`。
|
||||
|
||||
#### 使用`else if`实现多重条件
|
||||
#### 使用 `else if` 实现多重条件
|
||||
|
||||
可以将`else if`表达式与`if`和`else`组合来实现多重条件。例如:
|
||||
可以将 `else if` 表达式与 `if` 和 `else` 组合来实现多重条件。例如:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
@ -121,22 +121,22 @@ fn main() {
|
||||
|
||||
这个程序有四个可能的执行路径。运行后应该能看到如下输出:
|
||||
|
||||
```
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling branches v0.1.0 (file:///projects/branches)
|
||||
Running `target/debug/branches`
|
||||
number is divisible by 3
|
||||
```
|
||||
|
||||
当执行这个程序,它按顺序检查每个`if`表达式并执行第一个条件为真的代码块。注意即使 6 可以被 2 整除,也不会出现`number is divisible by 2`的输出,更不会出现`else`块中的`number is not divisible by 4, 3, or 2`。原因是 Rust 只会执行第一个条件为真的代码块,并且它一旦找到一个以后,就不会检查剩下的条件了。
|
||||
当执行这个程序,它按顺序检查每个 `if` 表达式并执行第一个条件为真的代码块。注意即使 6 可以被 2 整除,也不会出现 `number is divisible by 2` 的输出,更不会出现 `else` 块中的 `number is not divisible by 4, 3, or 2`。原因是 Rust 只会执行第一个条件为真的代码块,并且它一旦找到一个以后,就不会检查剩下的条件了。
|
||||
|
||||
使用过多的`else if`表达式会使代码显得杂乱无章,所以如果有多于一个`else if`,最好重构代码。为此第六章会介绍 Rust 中一个叫做`match`的强大的分支结构(branching construct)。
|
||||
使用过多的 `else if` 表达式会使代码显得杂乱无章,所以如果有多于一个 `else if`,最好重构代码。为此第六章会介绍 Rust 中一个叫做 `match` 的强大的分支结构(branching construct)。
|
||||
|
||||
#### 在`let`语句中使用`if`
|
||||
#### 在 `let` 语句中使用 `if`
|
||||
|
||||
因为`if`是一个表达式,我们可以在`let`语句的右侧使用它,例如在列表 3-4 中:
|
||||
因为 `if` 是一个表达式,我们可以在 `let` 语句的右侧使用它,例如在列表 3-4 中:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
@ -151,21 +151,20 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 3-4: Assigning the result of an `if` expression
|
||||
to a variable</span>
|
||||
<span class="caption">列表 3-4:将 `if` 的返回值赋值给一个变量</span>
|
||||
|
||||
`number`变量将会绑定到基于`if`表达式结果的值。运行这段代码看看会出现什么:
|
||||
`number` 变量将会绑定到基于 `if` 表达式结果的值。运行这段代码看看会出现什么:
|
||||
|
||||
```
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling branches v0.1.0 (file:///projects/branches)
|
||||
Running `target/debug/branches`
|
||||
The value of number is: 5
|
||||
```
|
||||
|
||||
还记得代码块的值是其最后一个表达式的值,以及数字本身也是一个表达式吗。在这个例子中,整个`if`表达式的值依赖哪个代码块被执行。这意味着`if`的每个分支的可能的返回值都必须是相同类型;在列表 3-4 中,`if`分支和`else`分支的结果都是`i32`整型。不过如果像下面的例子那样这些类型并不匹配会怎么样呢?
|
||||
还记得代码块的值是其最后一个表达式的值,以及数字本身也是一个表达式吗。在这个例子中,整个 `if` 表达式的值依赖哪个代码块被执行。这意味着 `if` 的每个分支的可能的返回值都必须是相同类型;在列表 3-4 中,`if` 分支和 `else` 分支的结果都是 `i32` 整型。不过如果像下面的例子那样这些类型并不匹配会怎么样呢?
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
fn main() {
|
||||
@ -181,39 +180,39 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
当运行这段代码,会得到一个错误。`if`和`else`分支的值类型是不相容的,同时 Rust 也准确地表明了在程序中的何处发现的这个问题:
|
||||
当运行这段代码,会得到一个错误。`if` 和 `else` 分支的值类型是不相容的,同时 Rust 也准确地表明了在程序中的何处发现的这个问题:
|
||||
|
||||
```
|
||||
```text
|
||||
error[E0308]: if and else have incompatible types
|
||||
--> src/main.rs:4:18
|
||||
|
|
||||
4 | let number = if condition {
|
||||
| __________________^ starting here...
|
||||
| __________________^
|
||||
5 | | 5
|
||||
6 | | } else {
|
||||
7 | | "six"
|
||||
8 | | };
|
||||
| |_____^ ...ending here: expected integral variable, found reference
|
||||
| |_____^ expected integral variable, found reference
|
||||
|
|
||||
= note: expected type `{integer}`
|
||||
found type `&'static str`
|
||||
```
|
||||
|
||||
`if`代码块的表达式返回一个整型,而`else`代码块返回一个字符串。这并不可行,因为变量必须只有一个类型。Rust 需要在编译时就确切的知道`number`变量的类型,这样它就可以在编译时证明其他使用`number`变量的地方它的类型是有效的。Rust 并不能够在`number`的类型只能在运行时确定的情况下工作;这样会使编译器变得更复杂而且只能为代码提供更少的保障,因为它不得不记录所有变量的多种可能的类型。
|
||||
`if` 代码块的表达式返回一个整型,而 `else` 代码块返回一个字符串。这并不可行,因为变量必须只有一个类型。Rust 需要在编译时就确切的知道 `number` 变量的类型,这样它就可以在编译时证明其他使用 `number` 变量的地方它的类型是有效的。Rust 并不能够在 `number` 的类型只能在运行时确定的情况下工作;这样会使编译器变得更复杂而且只能为代码提供更少的保障,因为它不得不记录所有变量的多种可能的类型。
|
||||
|
||||
### 使用循环重复执行
|
||||
|
||||
多次执行一段代码是很常用的。为了这个功能,Rust 提供了多种**循环**(*loops*)。一个循环执行循环体中的代码直到结尾并紧接着从回到开头继续执行。为了实验一下循环,让我们创建一个叫做 *loops* 的新项目。
|
||||
多次执行同一段代码是很常用的。为了这个功能,Rust 提供了多种 **循环**(*loops*)。一个循环执行循环体中的代码直到结尾并紧接着从回到开头继续执行。为了实验一下循环,让我们创建一个叫做 *loops* 的新项目。
|
||||
|
||||
Rust 有三种循环类型:`loop`、`while`和`for`。让我们每一个都试试。
|
||||
Rust 有三种循环类型:`loop`、`while` 和 `for`。让我们每一个都试试。
|
||||
|
||||
#### 使用`loop`重复执行代码
|
||||
#### 使用 `loop` 重复执行代码
|
||||
|
||||
`loop`关键字告诉 Rust 一遍又一遍的执行一段代码直到你明确要求停止。
|
||||
`loop` 关键字告诉 Rust 一遍又一遍的执行一段代码直到你明确要求停止。
|
||||
|
||||
作为一个例子,将 *loops* 目录中的 *src/main.rs* 文件修改为如下:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
fn main() {
|
||||
@ -223,9 +222,9 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
当执行这个程序,我们会看到`again!`被连续的打印直到我们手动停止程序.大部分终端都支持一个键盘快捷键,ctrl-C,来终止一个陷入无限循环的程序。尝试一下:
|
||||
当执行这个程序,我们会看到 `again!` 被连续的打印直到我们手动停止程序。大部分终端都支持一个键盘快捷键,<span class="keystroke">ctrl-C</span>,来终止一个陷入无限循环的程序。尝试一下:
|
||||
|
||||
```sh
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling loops v0.1.0 (file:///projects/loops)
|
||||
Running `target/debug/loops`
|
||||
@ -236,23 +235,23 @@ again!
|
||||
^Cagain!
|
||||
```
|
||||
|
||||
符号`^C`代表你在这按下了 ctrl-C。在`^C`之后你可能看到`again!`也可能看不到,这依赖于在接收到终止信号时代码执行到了循环的何处。
|
||||
符号 `^C` 代表你在这按下了<span class="keystroke">ctrl-C</span>。在 `^C` 之后你可能看到 `again!` 也可能看不到,这依赖于在接收到终止信号时代码执行到了循环的何处。
|
||||
|
||||
幸运的是,Rust 提供了另一个更可靠的方式来退出循环。可以使用`break`关键字来告诉程序何时停止执行循环。还记得我们在第二章猜猜看游戏的“猜测正确后退出”部分使用过它来在用户猜对数字赢得游戏后退出程序吗。
|
||||
幸运的是,Rust 提供了另一个更可靠的方式来退出循环。可以使用 `break` 关键字来告诉程序何时停止执行循环。还记得我们在第二章猜猜看游戏的 “猜测正确后退出” 部分使用过它来在用户猜对数字赢得游戏后退出程序吗。
|
||||
|
||||
#### `while`条件循环
|
||||
#### `while` 条件循环
|
||||
|
||||
在程序中计算循环的条件也很常见。当条件为真,执行循环。当条件不再为真,调用`break`停止循环。这个循环类型可以通过组合`loop`、`if`、`else`和`break`来实现;如果你喜欢的话,现在就可以在程序中试试。
|
||||
在程序中计算循环的条件也很常见。当条件为真,执行循环。当条件不再为真,调用 `break`停止循环。这个循环类型可以通过组合 `loop`、`if`、`else` 和 `break`来实现;如果你喜欢的话,现在就可以在程序中试试。
|
||||
|
||||
然而,这个模式太常见了以至于 Rust 为此提供了一个内建的语言结构,它被称为`while`循环。下面的例子使用了`while`:程序循环三次,每次数字都减一。接着,在循环之后,打印出另一个信息并退出:
|
||||
然而,这个模式太常见了以至于 Rust 为此提供了一个内建的语言结构,它被称为 `while` 循环。下面的例子使用了 `while`:程序循环三次,每次数字都减一。接着,在循环之后,打印出另一个信息并退出:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let mut number = 3;
|
||||
|
||||
while number != 0 {
|
||||
while number != 0 {
|
||||
println!("{}!", number);
|
||||
|
||||
number = number - 1;
|
||||
@ -262,13 +261,13 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
这个结构消除了很多需要嵌套使用`loop`、`if`、`else`和`break`的代码,这样显得更加清楚。当条件为真就执行,否则退出循环。
|
||||
这个结构消除了很多需要嵌套使用 `loop`、`if`、`else` 和 `break` 的代码,这样显得更加清楚。当条件为真就执行,否则退出循环。
|
||||
|
||||
#### 使用`for`遍历集合
|
||||
#### 使用 `for` 遍历集合
|
||||
|
||||
可以使用`while`结构来遍历一个元素集合,比如数组。例如:
|
||||
可以使用 `while` 结构来遍历一个元素集合,比如数组。如下:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
@ -283,12 +282,11 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 3-5: Looping through each element of a collection
|
||||
using a `while` loop</span>
|
||||
<span class="caption">列表 3-5:使用 `while` 循环遍历集合中的每一个元素</span>
|
||||
|
||||
这里代码对数组中的元素进行计数。它从索引`0`开始,并接着循环直到遇到数组的最后一个索引(这时,`index < 5`不再为真)。运行这段代码会打印出数组中的每一个元素:
|
||||
这里代码对数组中的元素进行计数。它从索引 `0` 开始,并接着循环直到遇到数组的最后一个索引(这时,`index < 5` 不再为真)。运行这段代码会打印出数组中的每一个元素:
|
||||
|
||||
```sh
|
||||
```text
|
||||
$ cargo run
|
||||
Compiling loops v0.1.0 (file:///projects/loops)
|
||||
Running `target/debug/loops`
|
||||
@ -299,13 +297,13 @@ the value is: 40
|
||||
the value is: 50
|
||||
```
|
||||
|
||||
所有数组中的五个元素都如期被打印出来。尽管`index`在某一时刻会到达值`5`,不过循环在其尝试从数组获取第六个值(会越界)之前就停止了。
|
||||
所有数组中的五个元素都如期被打印出来。尽管 `index` 在某一时刻会到达值 `5`,不过循环在其尝试从数组获取第六个值(会越界)之前就停止了。
|
||||
|
||||
不过这个过程是容易出错的;如果索引长度不正确会导致程序 panic。这也使程序更慢,因为编译器增加了运行时代码来对每次循环的每个元素进行条件检查。
|
||||
|
||||
可以使用`for`循环来对一个集合的每个元素执行一些代码,来作为一个更有效率替代。`for`循环看起来像这样:
|
||||
可以使用 `for` 循环来对一个集合的每个元素执行一些代码,来作为一个更有效率的替代。`for` 循环看起来像这样:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
@ -317,18 +315,17 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
<span class="caption">Listing 3-6: Looping through each element of a collection
|
||||
using a `for` loop</span>
|
||||
<span class="caption">列表 3-6:使用 `for` 循环遍历集合中的每一个元素</span>
|
||||
|
||||
当运行这段代码,将看到与列表 3-5 一样的输出。更为重要的是,我们增强了代码安全性并消除了出现可能会导致超出数组的结尾或遍历长度不够而缺少一些元素这类 bug 机会。
|
||||
当运行这段代码,将看到与列表 3-5 一样的输出。更为重要的是,我们增强了代码安全性并消除了出现可能会导致超出数组的结尾或遍历长度不够而缺少一些元素这类 bug 的机会。
|
||||
|
||||
例如,在列表 3-5 的代码中,如果从数组`a`中移除一个元素但忘记更新条件为`while index < 4`,代码将会 panic。使用`for`循环的话,就不需要惦记着在更新数组元素数量时修改其他的代码了。
|
||||
例如,在列表 3-5 的代码中,如果从数组 `a` 中移除一个元素但忘记更新条件为 `while index < 4`,代码将会 panic。使用`for`循环的话,就不需要惦记着在更新数组元素数量时修改其他的代码了。
|
||||
|
||||
`for`循环的安全性和简洁性使得它在成为 Rust 中使用最多的循环结构。即使是在想要循环执行代码特定次数时,例如列表 3-5 中使用`while`循环的倒计时例子,大部分 Rustacean 也会使用`for`循环。这么做的方式是使用`Range`,它是标准库提供的用来生成从一个数字开始到另一个数字结束的所有数字序列的类型。
|
||||
`for` 循环的安全性和简洁性使得它在成为 Rust 中使用最多的循环结构。即使是在想要循环执行代码特定次数时,例如列表 3-5 中使用 `while` 循环的倒计时例子,大部分 Rustacean 也会使用 `for` 循环。这么做的方式是使用 `Range`,它是标准库提供的用来生成从一个数字开始到另一个数字结束的所有数字序列的类型。
|
||||
|
||||
下面是一个使用`for`循环来倒计时的例子,它还使用了一个我们还未讲到的方法,`rev`,用来反转 range:
|
||||
下面是一个使用 `for` 循环来倒计时的例子,它还使用了一个我们还未讲到的方法,`rev`,用来反转 range:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
@ -343,10 +340,10 @@ fn main() {
|
||||
|
||||
## 总结
|
||||
|
||||
你做到了!这是一个相当可观的章节:你学习了变量,标量和`if`表达式,还有循环!如果你想要实践本章讨论的概念,尝试构建如下的程序:
|
||||
你做到了!这是一个相当可观的章节:你学习了变量,标量和 `if` 表达式,还有循环!如果你想要实践本章讨论的概念,尝试构建如下的程序:
|
||||
|
||||
* 相互转换摄氏与华氏温度
|
||||
* 生成 n 阶斐波那契数列
|
||||
* 打印圣诞颂歌“The Twelve Days of Christmas”的歌词,并利用歌曲中的重复部分(编写循环)
|
||||
* 打印圣诞颂歌 “The Twelve Days of Christmas” 的歌词,并利用歌曲中的重复部分(编写循环)
|
||||
|
||||
当你准备好继续的时候,让我们讨论一个其他语言中*并不*常见的概念:所有权(ownership)。
|
||||
当你准备好继续的时候,让我们讨论一个其他语言中 *并不* 常见的概念:所有权(ownership)。
|
Loading…
Reference in New Issue
Block a user