|
|
@ -1,10 +1,9 @@
|
|
|
|
## 数据类型
|
|
|
|
## 数据类型
|
|
|
|
|
|
|
|
|
|
|
|
> [ch03-02-data-types.md](https://github.com/rust-lang/book/blob/main/src/ch03-02-data-types.md)
|
|
|
|
<!-- https://github.com/rust-lang/book/blob/main/src/ch03-02-data-types.md -->
|
|
|
|
> <br>
|
|
|
|
<!-- commit a619cc5f073b1b59c026cf0f92ab061a46716325 -->
|
|
|
|
> commit d0acb2595c891de97a133d06635c50ab449dd65c
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
在 Rust 中,每一个值都属于某一个 **数据类型**(*data type*),这告诉 Rust 它被指定为何种数据,以便明确数据处理方式。我们将看到两类数据类型子集:标量(scalar)和复合(compound)。
|
|
|
|
在 Rust 中,每一个值都有一个特定 **数据类型**(*data type*),这告诉 Rust 它被指定为何种数据,以便明确数据处理方式。我们将看到两类数据类型子集:标量(scalar)和复合(compound)。
|
|
|
|
|
|
|
|
|
|
|
|
记住,Rust 是 **静态类型**(*statically typed*)语言,也就是说在编译时就必须知道所有变量的类型。根据值及其使用方式,编译器通常可以推断出我们想要用的类型。当多种类型均有可能时,比如第二章的 [“比较猜测的数字和秘密数字”][comparing-the-guess-to-the-secret-number] 使用 `parse` 将 `String` 转换为数字时,必须增加类型注解,像这样:
|
|
|
|
记住,Rust 是 **静态类型**(*statically typed*)语言,也就是说在编译时就必须知道所有变量的类型。根据值及其使用方式,编译器通常可以推断出我们想要用的类型。当多种类型均有可能时,比如第二章的 [“比较猜测的数字和秘密数字”][comparing-the-guess-to-the-secret-number] 使用 `parse` 将 `String` 转换为数字时,必须增加类型注解,像这样:
|
|
|
|
|
|
|
|
|
|
|
@ -26,7 +25,7 @@ let guess: u32 = "42".parse().expect("Not a number!");
|
|
|
|
|
|
|
|
|
|
|
|
#### 整型
|
|
|
|
#### 整型
|
|
|
|
|
|
|
|
|
|
|
|
**整数** 是一个没有小数部分的数字。我们在第二章使用过 `u32` 整数类型。该类型声明表明,它关联的值应该是一个占据 32 比特位的无符号整数(有符号整数类型以 `i` 开头而不是 `u`)。表格 3-1 展示了 Rust 内建的整数类型。我们可以使用其中的任一个来声明一个整数值的类型。
|
|
|
|
**整型** 是一个没有小数部分的数字。我们在第二章使用过 `u32` 整数类型。该类型声明表明,它关联的值应该是一个占据 32 比特位的无符号整数(有符号整数类型以 `i` 开头而不是 `u`)。表格 3-1 展示了 Rust 内建的整数类型。我们可以使用其中的任一个来声明一个整数值的类型。
|
|
|
|
|
|
|
|
|
|
|
|
<span class="caption">表格 3-1: Rust 中的整型</span>
|
|
|
|
<span class="caption">表格 3-1: Rust 中的整型</span>
|
|
|
|
|
|
|
|
|
|
|
@ -37,9 +36,9 @@ let guess: u32 = "42".parse().expect("Not a number!");
|
|
|
|
| 32-bit | `i32` | `u32` |
|
|
|
|
| 32-bit | `i32` | `u32` |
|
|
|
|
| 64-bit | `i64` | `u64` |
|
|
|
|
| 64-bit | `i64` | `u64` |
|
|
|
|
| 128-bit | `i128` | `u128` |
|
|
|
|
| 128-bit | `i128` | `u128` |
|
|
|
|
| arch | `isize` | `usize` |
|
|
|
|
| 架构相关 | `isize` | `usize` |
|
|
|
|
|
|
|
|
|
|
|
|
每一个变体都可以是有符号或无符号的,并有一个明确的大小。**有符号** 和 **无符号** 代表数字能否为负值,换句话说,这个数字是否有可能是负数(有符号数),或者永远为正而不需要符号(无符号数)。这有点像在纸上书写数字:当需要考虑符号的时候,数字以加号或减号作为前缀;然而,可以安全地假设为正数时,加号前缀通常省略。有符号数以[补码形式(two’s complement representation)][twos-complement] 存储。
|
|
|
|
每一个变体都可以是有符号或无符号的,并有一个明确的大小。**有符号** 和 **无符号** 代表数字能否为负值,换句话说,这个数字是否有可能是负数(有符号数),或者永远为正而不需要符号(无符号数)。这有点像在纸上书写数字:当需要考虑符号的时候,数字以加号或减号作为前缀;然而,可以安全地假设为正数时,加号前缀通常省略。有符号数以[二进制补码形式(two’s complement representation)][twos-complement] 存储。
|
|
|
|
|
|
|
|
|
|
|
|
每一个有符号的变体可以储存包含从 -(2<sup>n - 1</sup>) 到 2<sup>n - 1</sup> - 1 在内的数字,这里 *n* 是变体使用的位数。所以 `i8` 可以储存从 -(2<sup>7</sup>) 到 2<sup>7</sup> - 1 在内的数字,也就是从 -128 到 127。无符号的变体可以储存从 0 到 2<sup>n</sup> - 1 的数字,所以 `u8` 可以储存从 0 到 2<sup>8</sup> - 1 的数字,也就是从 0 到 255。
|
|
|
|
每一个有符号的变体可以储存包含从 -(2<sup>n - 1</sup>) 到 2<sup>n - 1</sup> - 1 在内的数字,这里 *n* 是变体使用的位数。所以 `i8` 可以储存从 -(2<sup>7</sup>) 到 2<sup>7</sup> - 1 在内的数字,也就是从 -128 到 127。无符号的变体可以储存从 0 到 2<sup>n</sup> - 1 的数字,所以 `u8` 可以储存从 0 到 2<sup>8</sup> - 1 的数字,也就是从 0 到 255。
|
|
|
|
|
|
|
|
|
|
|
@ -57,11 +56,11 @@ let guess: u32 = "42".parse().expect("Not a number!");
|
|
|
|
| Binary (二进制) | `0b1111_0000` |
|
|
|
|
| Binary (二进制) | `0b1111_0000` |
|
|
|
|
| Byte (单字节字符)(仅限于`u8`) | `b'A'` |
|
|
|
|
| Byte (单字节字符)(仅限于`u8`) | `b'A'` |
|
|
|
|
|
|
|
|
|
|
|
|
那么该使用哪种类型的数字呢?如果拿不定主意,Rust 的默认类型通常是个不错的起点,数字类型默认是 `i32`。`isize` 或 `usize` 主要作为某些集合的索引。
|
|
|
|
那么该使用哪种类型的数字呢?如果拿不定主意,Rust 的默认类型通常是个不错的起点,整型默认是 `i32`。`isize` 或 `usize` 主要作为某些集合的索引。
|
|
|
|
|
|
|
|
|
|
|
|
> ##### 整型溢出
|
|
|
|
> ##### 整型溢出
|
|
|
|
>
|
|
|
|
>
|
|
|
|
> 比方说有一个 `u8` ,它可以存放从零到 `255` 的值。那么当你将其修改为 `256` 时会发生什么呢?这被称为 “整型溢出”(“integer overflow” ),这会导致以下两种行为之一的发生。当在 debug 模式编译时,Rust 检查这类问题并使程序 *panic*,这个术语被 Rust 用来表明程序因错误而退出。第九章 [“`panic!` 与不可恢复的错误”][unrecoverable-errors-with-panic] 部分会详细介绍 panic。
|
|
|
|
> 比方说有一个 `u8` ,它可以存放从零到 `255` 的值。那么当你将其修改为 `256` 时就会发生 **整型溢出**(*integer overflow* ),这会导致以下两种行为之一的发生。当在 debug 模式编译时,Rust 检查这类问题并使程序 *panic*。*panic* 这个术语被 Rust 用来表明程序因错误而退出。第九章 [“`panic!` 与不可恢复的错误”][unrecoverable-errors-with-panic] 部分会详细介绍 panic。
|
|
|
|
>
|
|
|
|
>
|
|
|
|
> 使用 `--release` flag 在 release 模式中构建时,Rust **不会**检测会导致 panic 的整型溢出。相反发生整型溢出时,Rust 会进行一种被称为二进制补码 wrapping(*two’s complement wrapping*)的操作。简而言之,比此类型能容纳最大值还大的值会回绕到最小值,值 `256` 变成 `0`,值 `257` 变成 `1`,依此类推。程序不会 panic,不过变量可能也不会是你所期望的值。依赖整型溢出 wrapping 的行为被认为是一种错误。
|
|
|
|
> 使用 `--release` flag 在 release 模式中构建时,Rust **不会**检测会导致 panic 的整型溢出。相反发生整型溢出时,Rust 会进行一种被称为二进制补码 wrapping(*two’s complement wrapping*)的操作。简而言之,比此类型能容纳最大值还大的值会回绕到最小值,值 `256` 变成 `0`,值 `257` 变成 `1`,依此类推。程序不会 panic,不过变量可能也不会是你所期望的值。依赖整型溢出 wrapping 的行为被认为是一种错误。
|
|
|
|
>
|
|
|
|
>
|
|
|
@ -75,19 +74,17 @@ let guess: u32 = "42".parse().expect("Not a number!");
|
|
|
|
|
|
|
|
|
|
|
|
Rust 也有两个原生的 **浮点数**(*floating-point numbers*)类型,它们是带小数点的数字。Rust 的浮点数类型是 `f32` 和 `f64`,分别占 32 位和 64 位。默认类型是 `f64`,因为在现代 CPU 中,它与 `f32` 速度几乎一样,不过精度更高。所有的浮点型都是有符号的。
|
|
|
|
Rust 也有两个原生的 **浮点数**(*floating-point numbers*)类型,它们是带小数点的数字。Rust 的浮点数类型是 `f32` 和 `f64`,分别占 32 位和 64 位。默认类型是 `f64`,因为在现代 CPU 中,它与 `f32` 速度几乎一样,不过精度更高。所有的浮点型都是有符号的。
|
|
|
|
|
|
|
|
|
|
|
|
这是一个展示浮点数的实例:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<span class="filename">文件名:src/main.rs</span>
|
|
|
|
<span class="filename">文件名:src/main.rs</span>
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
|
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-06-floating-point/src/main.rs}}
|
|
|
|
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-06-floating-point/src/main.rs}}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
浮点数采用 IEEE-754 标准表示。`f32` 是单精度浮点数,`f64` 是双精度浮点数。
|
|
|
|
浮点数采用 IEEE-754 标准表示。(`f32` 是单精度浮点数,`f64` 是双精度浮点数。)
|
|
|
|
|
|
|
|
|
|
|
|
#### 数值运算
|
|
|
|
#### 数值运算
|
|
|
|
|
|
|
|
|
|
|
|
Rust 中的所有数字类型都支持基本数学运算:加法、减法、乘法、除法和取余。整数除法会向零舍入到最接近的整数。下面的代码展示了如何在 `let` 语句中使用它们:
|
|
|
|
Rust 中的所有数字类型都支持基本数学运算:加法、减法、乘法、除法和取余。整数除法会向零舍入到最接近的整数。下面的代码展示了如何在 `let` 语句中使用各种数值运算:
|
|
|
|
|
|
|
|
|
|
|
|
<span class="filename">文件名:src/main.rs</span>
|
|
|
|
<span class="filename">文件名:src/main.rs</span>
|
|
|
|
|
|
|
|
|
|
|
@ -97,7 +94,7 @@ Rust 中的所有数字类型都支持基本数学运算:加法、减法、乘
|
|
|
|
|
|
|
|
|
|
|
|
这些语句中的每个表达式使用了一个数学运算符并计算出了一个值,然后绑定给一个变量。[附录 B][appendix_b]<!-- ignore --> 包含 Rust 提供的所有运算符的列表。
|
|
|
|
这些语句中的每个表达式使用了一个数学运算符并计算出了一个值,然后绑定给一个变量。[附录 B][appendix_b]<!-- ignore --> 包含 Rust 提供的所有运算符的列表。
|
|
|
|
|
|
|
|
|
|
|
|
#### 布尔型
|
|
|
|
#### 布尔类型
|
|
|
|
|
|
|
|
|
|
|
|
正如其他大部分编程语言一样,Rust 中的布尔类型有两个可能的值:`true` 和 `false`。Rust 中的布尔类型使用 `bool` 表示。例如:
|
|
|
|
正如其他大部分编程语言一样,Rust 中的布尔类型有两个可能的值:`true` 和 `false`。Rust 中的布尔类型使用 `bool` 表示。例如:
|
|
|
|
|
|
|
|
|
|
|
@ -111,7 +108,7 @@ Rust 中的所有数字类型都支持基本数学运算:加法、减法、乘
|
|
|
|
|
|
|
|
|
|
|
|
#### 字符类型
|
|
|
|
#### 字符类型
|
|
|
|
|
|
|
|
|
|
|
|
Rust 的 `char` 类型是语言中最原生的字母类型。下面是一些声明 `char` 值的例子:
|
|
|
|
Rust 的 `char` 类型是语言中最原始的字母类型。下面是一些声明 `char` 值的例子:
|
|
|
|
|
|
|
|
|
|
|
|
<span class="filename">文件名:src/main.rs</span>
|
|
|
|
<span class="filename">文件名:src/main.rs</span>
|
|
|
|
|
|
|
|
|
|
|
@ -119,7 +116,7 @@ Rust 的 `char` 类型是语言中最原生的字母类型。下面是一些声
|
|
|
|
{{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-09-char/src/main.rs}}
|
|
|
|
{{#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] 中将详细讨论这个主题。
|
|
|
|
注意,我们用单引号声明 `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] 中将详细讨论这个主题。
|
|
|
|
|
|
|
|
|
|
|
|
### 复合类型
|
|
|
|
### 复合类型
|
|
|
|
|
|
|
|
|
|
|
@ -127,7 +124,7 @@ Rust 的 `char` 类型是语言中最原生的字母类型。下面是一些声
|
|
|
|
|
|
|
|
|
|
|
|
#### 元组类型
|
|
|
|
#### 元组类型
|
|
|
|
|
|
|
|
|
|
|
|
元组是一个将多个其他类型的值组合进一个复合类型的主要方式。元组长度固定:一旦声明,其长度不会增大或缩小。
|
|
|
|
元组是一个将多个不同类型的值组合进一个复合类型的主要方式。元组长度固定:一旦声明,其长度不会增大或缩小。
|
|
|
|
|
|
|
|
|
|
|
|
我们使用包含在圆括号中的逗号分隔的值列表来创建一个元组。元组中的每一个位置都有一个类型,而且这些不同值的类型也不必是相同的。这个例子中使用了可选的类型注解:
|
|
|
|
我们使用包含在圆括号中的逗号分隔的值列表来创建一个元组。元组中的每一个位置都有一个类型,而且这些不同值的类型也不必是相同的。这个例子中使用了可选的类型注解:
|
|
|
|
|
|
|
|
|
|
|
@ -147,7 +144,7 @@ Rust 的 `char` 类型是语言中最原生的字母类型。下面是一些声
|
|
|
|
|
|
|
|
|
|
|
|
程序首先创建了一个元组并绑定到 `tup` 变量上。接着使用了 `let` 和一个模式将 `tup` 分成了三个不同的变量,`x`、`y` 和 `z`。这叫做 **解构**(*destructuring*),因为它将一个元组拆成了三个部分。最后,程序打印出了 `y` 的值,也就是 `6.4`。
|
|
|
|
程序首先创建了一个元组并绑定到 `tup` 变量上。接着使用了 `let` 和一个模式将 `tup` 分成了三个不同的变量,`x`、`y` 和 `z`。这叫做 **解构**(*destructuring*),因为它将一个元组拆成了三个部分。最后,程序打印出了 `y` 的值,也就是 `6.4`。
|
|
|
|
|
|
|
|
|
|
|
|
我们也可以使用点号(`.`)后跟值的索引来直接访问它们。例如:
|
|
|
|
我们也可以使用点号(`.`)后跟值的索引来直接访问所需的元组元素。例如:
|
|
|
|
|
|
|
|
|
|
|
|
<span class="filename">文件名:src/main.rs</span>
|
|
|
|
<span class="filename">文件名:src/main.rs</span>
|
|
|
|
|
|
|
|
|
|
|
@ -163,7 +160,7 @@ Rust 的 `char` 类型是语言中最原生的字母类型。下面是一些声
|
|
|
|
|
|
|
|
|
|
|
|
另一个包含多个值的方式是 **数组**(*array*)。与元组不同,数组中的每个元素的类型必须相同。Rust 中的数组与一些其他语言中的数组不同,Rust 中的数组长度是固定的。
|
|
|
|
另一个包含多个值的方式是 **数组**(*array*)。与元组不同,数组中的每个元素的类型必须相同。Rust 中的数组与一些其他语言中的数组不同,Rust 中的数组长度是固定的。
|
|
|
|
|
|
|
|
|
|
|
|
我们将数组的值写成在方括号内,用逗号分隔:
|
|
|
|
我们将数组的值写成在方括号内,用逗号分隔的列表:
|
|
|
|
|
|
|
|
|
|
|
|
<span class="filename">文件名:src/main.rs</span>
|
|
|
|
<span class="filename">文件名:src/main.rs</span>
|
|
|
|
|
|
|
|
|
|
|
@ -221,7 +218,8 @@ let a = [3; 5];
|
|
|
|
此代码编译成功。如果您使用 `cargo run` 运行此代码并输入 `0`、`1`、`2`、`3` 或 `4`,程序将在数组中的索引处打印出相应的值。如果你输入一个超过数组末端的数字,如 10,你会看到这样的输出:
|
|
|
|
此代码编译成功。如果您使用 `cargo run` 运行此代码并输入 `0`、`1`、`2`、`3` 或 `4`,程序将在数组中的索引处打印出相应的值。如果你输入一个超过数组末端的数字,如 10,你会看到这样的输出:
|
|
|
|
|
|
|
|
|
|
|
|
```console
|
|
|
|
```console
|
|
|
|
thread 'main' panicked at 'index out of bounds: the len is 5 but the index is 10', src/main.rs:19:19
|
|
|
|
thread 'main' panicked at src/main.rs:19:19:
|
|
|
|
|
|
|
|
index out of bounds: the len is 5 but the index is 10
|
|
|
|
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
|
|
|
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|