@ -2,11 +2,11 @@
> [ch03-02-data-types.md ](https://github.com/rust-lang/book/blob/master/src/ch03-02-data-types.md )
> [ch03-02-data-types.md ](https://github.com/rust-lang/book/blob/master/src/ch03-02-data-types.md )
> < br >
> < br >
> commit a86c1d315789b3ca13b20d50ad5005c62bdd9e37
> commit 6598d3abac05ed1d0c45db92466ea49346d05e40
在 Rust 中,每一个值都属于某一个 ** 数据类型**( *data type*),这告诉 Rust 它被指定为何种数据, 以便明确数据处理方式。我们将看到两类数据类型子集: 标量( scalar) 和复合( compound) 。
在 Rust 中,每一个值都属于某一个 ** 数据类型**( *data type*),这告诉 Rust 它被指定为何种数据, 以便明确数据处理方式。我们将看到两类数据类型子集: 标量( scalar) 和复合( compound) 。
记住, Rust 是 ** 静态类型**( *statically typed*)语言,也就是说在编译时就必须知道所有变量的类型。根据值及其使用方式,编译器通常可以推断出我们想要用的类型。当多种类型均有可能时,比如第二章的 “比较猜测的数字和秘密数字” 使用 `parse` 将 `String` 转换为数字时,必须增加类型注解,像这样:
记住, Rust 是 ** 静态类型**( *statically typed*)语言,也就是说在编译时就必须知道所有变量的类型。根据值及其使用方式,编译器通常可以推断出我们想要用的类型。当多种类型均有可能时,比如第二章的 [ “比较猜测的数字和秘密数字”][comparing-the-guess-to-the-secret-number] 使用 `parse` 将 `String` 转换为数字时,必须增加类型注解,像这样:
```rust
```rust
let guess: u32 = "42".parse().expect("Not a number!");
let guess: u32 = "42".parse().expect("Not a number!");
@ -38,14 +38,15 @@ error[E0282]: type annotations needed
< span class = "caption" > 表格 3-1: Rust 中的整型< / span >
< span class = "caption" > 表格 3-1: Rust 中的整型< / span >
| 长度 | 有符号 | 无符号 |
| 长度 | 有符号 | 无符号 |
|--------|---------|----------|
|---------|---------|----------|
| 8-bit | `i8` | `u8` |
| 8-bit | `i8` | `u8` |
| 16-bit | `i16` | `u16` |
| 16-bit | `i16` | `u16` |
| 32-bit | `i32` | `u32` |
| 32-bit | `i32` | `u32` |
| 64-bit | `i64` | `u64` |
| 64-bit | `i64` | `u64` |
| arch | `isize` | `usize` |
| 128-bit | `i128` | `u128` |
| arch | `isize` | `usize` |
每一个变体都可以是有符号或无符号的,并有一个明确的大小。**有符号** 和 ** 无符号** 代表数字能否为负值, 换句话说, 数字是否需要有一个符号( 有符号数) , 或者永远为正而不需要符号( 无符号数) 。这有点像在纸上书写数字: 当需要考虑符号的时候, 数字以加号或减号作为前缀; 然而, 可以安全地假设为正数时, 加号前缀通常省略。有符号数以补码形式( two’ s complement representation) 存储(如果你不清楚这是什么,可以在网上搜索;对其的解释超出了本书的范畴) 。
每一个变体都可以是有符号或无符号的,并有一个明确的大小。**有符号** 和 ** 无符号** 代表数字能否为负值,换句话说,数字是否需要有一个符号(有符号数),或者永远为正而不需要符号(无符号数)。这有点像在纸上书写数字:当需要考虑符号的时候,数字以加号或减号作为前缀;然而,可以安全地假设为正数时,加号前缀通常省略。有符号数以[ 补码形式( two’ s complement representation) ](https://en.wikipedia.org/wiki/Two%27s_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。
@ -65,11 +66,11 @@ error[E0282]: type annotations needed
那么该使用哪种类型的数字呢? 如果拿不定主意, Rust 的默认类型通常就很好,数字类型默认是 `i32` :它通常是最快的,甚至在 64 位系统上也是。`isize` 或 `usize` 主要作为某些集合的索引。
那么该使用哪种类型的数字呢? 如果拿不定主意, Rust 的默认类型通常就很好,数字类型默认是 `i32` :它通常是最快的,甚至在 64 位系统上也是。`isize` 或 `usize` 主要作为某些集合的索引。
##### 整型溢出
> ##### 整型溢出
>
比方说有一个 `u8` ,它可以存放从零到 `255` 的值。那么当你将其修改为 `256` 时会发生什么呢?这被称为 “整型溢出”( “integer overflow” ),关于这一行为 Rust 有一些有趣的规则。当在 debug 模式编译时, Rust 检查这类问题并使程序 *panic* ,这个术语被 Rust 用来表明程序因错误而退出。第九章会详细介绍 panic。
> 比方说有一个 `u8` ,它可以存放从零到 `255` 的值。那么当你将其修改为 `256` 时会发生什么呢?这被称为 “整型溢出”( “integer overflow” ),关于这一行为 Rust 有一些有趣的规则。当在 debug 模式编译时, Rust 检查这类问题并使程序 *panic* ,这个术语被 Rust 用来表明程序因错误而退出。第九章 [“`panic!` 与不可恢复的错误”][unrecoverable-errors-with-panic] 部分 会详细介绍 panic。
>
在 release 构建中, Rust 不检测溢出,相反会进行一种被称为二进制补码(*two’ s complement wrapping*)的操作。简而言之,`256` 变成 `0` , `257` 变成 `1` ,依此类推。依赖整型溢出被认为是一种错误,即便可能出现这种行为。如果你确实需要这种行为,标准库中有一个类型显式提供此功能,`Wrapping`。
> 在 release 构建中, Rust 不检测溢出,相反会进行一种被称为二进制补码包装 ( *two’ s complement wrapping*)的操作。简而言之,`256` 变成 `0` , `257` 变成 `1` ,依此类推。依赖整型溢出被认为是一种错误,即便可能出现这种行为。如果你确实需要这种行为,标准库中有一个类型显式提供此功能,[ `Wrapping`][wrapping] 。
#### 浮点型
#### 浮点型
@ -130,7 +131,7 @@ fn main() {
}
}
```
```
使用布尔值的主要场景是条件表达式,例如 `if` 表达式。在 “控制流”( “Control Flow”) 部分将介绍 `if` 表达式在 Rust 中如何工作。
使用布尔值的主要场景是条件表达式,例如 `if` 表达式。在 [ “控制流”( “Control Flow”) ][control-flow] 部分将介绍 `if` 表达式在 Rust 中如何工作。
#### 字符类型
#### 字符类型
@ -146,7 +147,7 @@ fn main() {
}
}
```
```
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` 并不符合。第八章的 “字符串” 中将详细讨论这个主题。
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] 中将详细讨论这个主题。
### 复合类型
### 复合类型
@ -154,7 +155,7 @@ Rust 的 `char` 类型的大小为四个字节(four bytes),并代表了一个
#### 元组类型
#### 元组类型
元组是一个将多个其他类型的值组合进一个复合类型的主要方式。
元组是一个将多个其他类型的值组合进一个复合类型的主要方式。元组长度固定:一旦声明,其长度不会增大或缩小。
我们使用包含在圆括号中的逗号分隔的值列表来创建一个元组。元组中的每一个位置都有一个类型,而且这些不同值的类型也不必是相同的。这个例子中使用了可选的类型注解:
我们使用包含在圆括号中的逗号分隔的值列表来创建一个元组。元组中的每一个位置都有一个类型,而且这些不同值的类型也不必是相同的。这个例子中使用了可选的类型注解:
@ -223,13 +224,19 @@ let months = ["January", "February", "March", "April", "May", "June", "July",
"August", "September", "October", "November", "December"];
"August", "September", "October", "November", "December"];
```
```
数组的类型比较有趣;它看起来像 `[type; number]` 。例如:
可以像这样编写数组的类型:在方括号中包含每个元素的类型,后跟分号,再后跟数组元素的数量。
```rust
```rust
let a: [i32; 5] = [1, 2, 3, 4, 5];
let a: [i32; 5] = [1, 2, 3, 4, 5];
```
```
首先是方括号;这看起来像创建数组的语法。其中有两部分由分号分割的信息。第一部分是数组中每个元素的类型。因为所有元素都是相同类型的,所以只需列出一次。分号之后,是一个表示数组长度的数字。因为数组是固定长度的,该数字也一直保持不变,即便数组的元素被修改,它也不会增长或缩小。
这里,`i32` 是每个元素的类型。分号之后,数字 `5` 表明该数组包含五个元素。
这样编写数组的类型类似于另一个初始化数组的语法:如果你希望创建一个每个元素都相同的数组,可以在中括号内指定其初始值,后跟分号,再后跟数组的长度,如下所示:
```rust
let a = [3; 5];
```
##### 访问数组元素
##### 访问数组元素
@ -254,7 +261,7 @@ fn main() {
< span class = "filename" > 文件名: src/main.rs< / span >
< span class = "filename" > 文件名: src/main.rs< / span >
```rust,ignore
```rust,ignore,panics
fn main() {
fn main() {
let a = [1, 2, 3, 4, 5];
let a = [1, 2, 3, 4, 5];
let index = 10;
let index = 10;
@ -272,11 +279,18 @@ $ cargo run
Compiling arrays v0.1.0 (file:///projects/arrays)
Compiling arrays v0.1.0 (file:///projects/arrays)
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
Running `target/debug/arrays`
Running `target/debug/arrays`
thread '< main> ' panicked at 'index out of bounds: the len is 5 but the index is
thread 'main' panicked at 'index out of bounds: the len is 5 but the index is
10', src/main.rs:6
10', src/main.rs:5:19
note: Run with `RUST_BACKTRACE=1` for a backtrace.
note: Run with `RUST_BACKTRACE=1` for a backtrace.
```
```
编译并没有产生任何错误,不过程序会出现一个 ** 运行时**( *runtime*) 错误并且不会成功退出。当尝试用索引访问一个元素时, Rust 会检查指定的索引是否小于数组的长度。如果索引超出了数组长度, Rust 会 *panic* ,这是 Rust 术语,它用于程序因为错误而退出的情况。
编译并没有产生任何错误,不过程序会出现一个 ** 运行时**( *runtime*) 错误并且不会成功退出。当尝试用索引访问一个元素时, Rust 会检查指定的索引是否小于数组的长度。如果索引超出了数组长度, Rust 会 *panic* ,这是 Rust 术语,它用于程序因为错误而退出的情况。
这是第一个在实战中遇到的 Rust 安全原则的例子。在很多底层语言中, 并没有进行这类检查, 这样当提供了一个不正确的索引时, 就会访问无效的内存。通过立即退出而不是允许内存访问并继续执行, Rust 让你避开此类错误。第九章会讨论更多 Rust 的错误处理。
这是第一个在实战中遇到的 Rust 安全原则的例子。在很多底层语言中, 并没有进行这类检查, 这样当提供了一个不正确的索引时, 就会访问无效的内存。通过立即退出而不是允许内存访问并继续执行, Rust 让你避开此类错误。第九章会讨论更多 Rust 的错误处理。
[comparing-the-guess-to-the-secret-number]:
ch02-00-guessing-game-tutorial.html#comparing-the-guess-to-the-secret-number
[control-flow]: ch03-05-control-flow.html#control-flow
[strings]: ch08-02-strings.html#storing-utf-8-encoded-text-with-strings
[unrecoverable-errors-with-panic]: ch09-01-unrecoverable-errors-with-panic.html
[wrapping]: ../std/num/struct.Wrapping.html