diff --git a/projects/array_demo/Cargo.toml b/projects/array_demo/Cargo.toml new file mode 100644 index 0000000..2f7ca56 --- /dev/null +++ b/projects/array_demo/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "array_demo" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/projects/array_demo/src/main.rs b/projects/array_demo/src/main.rs new file mode 100644 index 0000000..ab3516a --- /dev/null +++ b/projects/array_demo/src/main.rs @@ -0,0 +1,13 @@ +fn main() { + let a = [1, 2, 3, 4, 5]; + + let first = a[0]; + let second = a[1]; + + println! (" + 数组 a:{:?} + 数组的第一个元素:{first} + 第二个元素:{second} + a[0]: {a[0]} + ", a); +} diff --git a/projects/boolean_types/Cargo.toml b/projects/boolean_types/Cargo.toml new file mode 100644 index 0000000..4349f44 --- /dev/null +++ b/projects/boolean_types/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "boolean_types" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/projects/boolean_types/src/main.rs b/projects/boolean_types/src/main.rs new file mode 100644 index 0000000..7440cc2 --- /dev/null +++ b/projects/boolean_types/src/main.rs @@ -0,0 +1,8 @@ +fn main() { + let t = true; + + let f: bool = false; // 带有显式类型注解 + + + println! ("t: {t}\nf: {f}"); +} diff --git a/projects/char_type/Cargo.toml b/projects/char_type/Cargo.toml new file mode 100644 index 0000000..cddca00 --- /dev/null +++ b/projects/char_type/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "char_type" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/projects/char_type/src/main.rs b/projects/char_type/src/main.rs new file mode 100644 index 0000000..0365a75 --- /dev/null +++ b/projects/char_type/src/main.rs @@ -0,0 +1,11 @@ +fn main() { + let c = 'z'; + let z: char = 'ℤ'; // 带有显式的类型注解 + let heart_eyed_cat = '😻'; + + println! (" + c 为 {c} + z 为 {z} + 爱心猫: {heart_eyed_cat} + "); +} diff --git a/projects/float_numbers/Cargo.toml b/projects/float_numbers/Cargo.toml new file mode 100644 index 0000000..632a5d0 --- /dev/null +++ b/projects/float_numbers/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "float_numbers" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/projects/float_numbers/src/main.rs b/projects/float_numbers/src/main.rs new file mode 100644 index 0000000..6f4f0fe --- /dev/null +++ b/projects/float_numbers/src/main.rs @@ -0,0 +1,5 @@ +fn main() { + let x = 2.0; // f64 + + let y: f32 = 3.0; // f32 +} diff --git a/projects/invalid_array_element_access/Cargo.toml b/projects/invalid_array_element_access/Cargo.toml new file mode 100644 index 0000000..29cd77e --- /dev/null +++ b/projects/invalid_array_element_access/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "invalid_array_element_access" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/projects/invalid_array_element_access/src/main.rs b/projects/invalid_array_element_access/src/main.rs new file mode 100644 index 0000000..dbea96d --- /dev/null +++ b/projects/invalid_array_element_access/src/main.rs @@ -0,0 +1,22 @@ +use std::io; + +fn main() { + let a = [1, 2, 3, 4, 5]; + + println! ("请输入一个数组索引。"); + + let mut index = String::new(); + + io::stdin() + .read_line(&mut index) + .expect("读取行失败,failed to read line"); + + let index: usize = index + .trim() + .parse() + .expect("输入的索引并非一个数字"); + + let element = a[index]; + + println! ("位于索引 {index} 出的元素值为:{element}"); +} diff --git a/projects/numeric_op/Cargo.toml b/projects/numeric_op/Cargo.toml new file mode 100644 index 0000000..6e69333 --- /dev/null +++ b/projects/numeric_op/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "numeric_op" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/projects/numeric_op/src/main.rs b/projects/numeric_op/src/main.rs new file mode 100644 index 0000000..bde5761 --- /dev/null +++ b/projects/numeric_op/src/main.rs @@ -0,0 +1,25 @@ +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 truncated = -5 / 3; // Results in -1 + + // remainder + let remainder = 43 % 5; + + println! (" + 5 + 10 = {sum}, + 95.5 - 4.3 = {difference} + 4 * 30 = {product} + 56.7 / 32.2 = {quotient} + -5 / 3 = {truncated} + 43 % 5 = {remainder}"); +} diff --git a/projects/tuple_demo/Cargo.toml b/projects/tuple_demo/Cargo.toml index 96a8535..446a340 100644 --- a/projects/tuple_demo/Cargo.toml +++ b/projects/tuple_demo/Cargo.toml @@ -1,8 +1,6 @@ [package] name = "tuple_demo" version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +edition = "2024" [dependencies] diff --git a/projects/tuple_demo/src/main.rs b/projects/tuple_demo/src/main.rs index 1211a24..6a41de0 100644 --- a/projects/tuple_demo/src/main.rs +++ b/projects/tuple_demo/src/main.rs @@ -1,22 +1,16 @@ -use std::io; - fn main() { - let a = [1, 2, 3, 4, 5]; + let x = (500, 6.4, 1); - println! ("请输入一个数组索引。"); + let five_hundred = x.0; - let mut index = String::new(); + let six_point_four = x.1; - io::stdin() - .read_line(&mut index) - .expect("读取行失败,failed to read line"); + let one = x.2; - let index: usize = index - .trim() - .parse() - .expect("输入的所以并非一个数字"); - - let element = a[index]; - - println! ("位于索引 {index} 出的元素值为:{element}"); + println! (" + x: {:?} + x.0:{five_hundred} + x.1:{six_point_four} + x.2:{one} + ", x) } diff --git a/src/Ch02_Programming_a_Guessing_Game.md b/src/Ch02_Programming_a_Guessing_Game.md index 712e573..cba4253 100644 --- a/src/Ch02_Programming_a_Guessing_Game.md +++ b/src/Ch02_Programming_a_Guessing_Game.md @@ -592,7 +592,7 @@ error: could not compile `guessing_game` (bin "guessing_game") due to 1 previous } ``` -该行为: +这一行为: ```rust let guess: u32 = guess.trim().parse().expect("请输入一个数字!"); diff --git a/src/SUMMARY.md b/src/SUMMARY.md index ede2a61..b9f7fe1 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -47,12 +47,12 @@ - [常用集合数据结构](Ch08_Common_Collections.md) - [使用矢量值存储值的清单](common_collections/vectors.md) - - [使用字符串存储 UTF-8 编码的文本](common_collections/strings.md) + - [以字符串存储 UTF-8 编码的文本](common_collections/strings.md) - [在哈希图中存储带有关联值的键](common_collections/hash_maps.md) - [错误处理](Ch09_Error_Handling.md) - - [使用 `panic!` 宏的不可恢复错误](error_handling/panic.md) - - [使用 `Result` 的可恢复错误](error_handling/result.md) + - [`panic!` 下的不可恢复错误](error_handling/panic.md) + - [`Result` 下的可恢复错误](error_handling/result.md) - [要 `panic!` 还是不要 `panic!`](error_handling/panic_or_not.md) --- diff --git a/src/common_collections/strings.md b/src/common_collections/strings.md index ddc2f81..faa6f31 100644 --- a/src/common_collections/strings.md +++ b/src/common_collections/strings.md @@ -1,4 +1,4 @@ -# 使用 `String` 存储 UTF-8 编码的文本 +# 以字符串存储 UTF-8 编码的文本 在第 4 章中,我们曾讨论过字符串,现在我们将对其进行更深入的研究。新的 Rust 公民通常卡在字符串上,原因有三: diff --git a/src/error_handling/panic.md b/src/error_handling/panic.md index 2bb6460..4e88462 100644 --- a/src/error_handling/panic.md +++ b/src/error_handling/panic.md @@ -1,4 +1,4 @@ -# 使用 `panic!` 的不可恢复错误 +# `panic!` 下的不可恢复错误 **Unrecoverable Errors with `panic!`** diff --git a/src/programming_concepts/data_types.md b/src/programming_concepts/data_types.md index 61100cb..372c5cc 100644 --- a/src/programming_concepts/data_types.md +++ b/src/programming_concepts/data_types.md @@ -36,17 +36,22 @@ error: could not compile `type_annotation` (bin "type_annotation") due to 1 prev ## 标量类型 -所谓 *标量,scalar* 类型,表示单个值。Rust 有四种主要的标量类型:整数、浮点数、布尔值和字符。咱们可能在其他编程语言中,见过这些类型。我们来了解一下,他们在 Rust 中是如何工作的。 +所谓 *标量,scalar* 类型,表示单个值。Rust 有四种主要的标量类型: + +- 整数 +- 浮点数 +- 布尔值 +- 和字符 + +咱们可能在别的编程语言中见过这些类型。我们来了解一下他们在 Rust 中的工作原理。 -### 整形 +### 整型 -**Integer Types** - - -所谓 *整数,integer*,是没有小数部分的数字。我们在第 2 章中,曾使用了一种整数类型,即 `u32` 类型。该类型声明,表示与之关联的值,应该是个无符号整数(有符号整数类型以 `i`,而不是 `u` 开头),占用 32 位空间。下表 3-1 列出了 Rust 中,内置的整数类型。我们可以使用任何的这些变种,来声明某个整数值的类型。 +所谓 *整数,integer*,属于不带小数部分的数字。我们曾在第 2 章中用到一种整数类型,即 `u32` 类型。这种类型声明表明,与之关联的值应是个占用 32 位空间的无符号整数(有符号整数类型以 `i` 而不是 `u` 开头)。下表 3-1 列出了 Rust 中内置的整数类型。我们可使用任何的这些变种,声明某个整数值的类型。 + *表 3-1:Rust 中的整数类型* @@ -57,55 +62,49 @@ error: could not compile `type_annotation` (bin "type_annotation") due to 1 prev | 32 位 | `i32` | `u32` | | 64 位 | `i64` | `u64` | | 128 位 | `i128` | `u128` | -| 架构(arch) | `isize` | `usize` | +| 依赖于架构(arch) | `isize` | `usize` | -其中每个变种,都可以是有符号或无符号的,并有明确的大小。*有符号,signed* 和 *无符号,unsigned*,指的是数字是否有可能是负数 -- 换句话说,数字是否需要带有符号(有符号),或者数字是否只能是正数,而因此可以不带符号表示(无符号)。这就像在纸上书写数字:当符号很重要时,数字会用加号或减号表示;然而,当可以肯定数字是正数时,数字就会不带符号给出。有符号的数字使用 [二进制补码表示法](https://en.wikipedia.org/wiki/Two%27s_complement) 存储。 +每个变种都可以是有符号或无符号的,并有着明确大小(译注:位数)。*有符号,signed* 与 *无符号,unsigned*,指的是数字是否可能是负数 -- 换句话说,数字是否需要带有符号(`signed`),或者数字是否将只会是正数而因此可在不带符号下表示(`unsigned`)。这就像在纸上书写数字:当符号很重要时,数字会以一个加号或减号表示;然而,当可以安全地假设该数字是正数时,他就会以无符号给出。有符号数字是使用 [二进制补码](https://en.wikipedia.org/wiki/Two%27s_complement) 表示法存储的。 -每个带符号的变种,可以存储 -(2n - 1) 到 2n - 1 - 1(含)的数字,其中 *n* 是该变种使用的比特数。因此,`i8` 可以存储-(27) 到 27 - 1 的数字,相当于 -128 到 127。无符号变种可以存储 0 至 2n - 1 的数字,因此 `u8` 可以存储 0 至 28 - 1 的数字,相当于 0 至 255。 +每个有符号变种都可以存储 -(2n - 1) 到 2n - 1 - 1(含)的数字,其中 *n* 是该变种用到的比特数。因此,`i8` 可以存储 -(27) 到 27 - 1 的数字,相当于 -128 到 127。无符号变种可存储 0 至 2n - 1 的数字,因此 `u8` 可存储 0 至 28 - 1 的数字,相当于 0 至 255。 -此外,`isize` 和 `usize` 这两种类型,取决于咱们程序运行所在的计算机体系结构,这在该表中,用 “架构(arch)” 表示:如果咱们在 64 位架构上,则为 64 位;如果是在 32 位架构上,则为 32 位。 +此外,`isize` 与 `usize` 两种类型,取决于咱们程序运行所在计算机的体系结构:当咱们是在某种 64 位架构上时就是 64 位,当咱们是在某种 32 位架构上时则为 32 位。 -咱们可以下表 3-2 所示的任何形式,写出整数的字面值。需要注意的是,可以是多种数值类型的数字字面值,则允许使用类型后缀,如 `57u8`,来指定类型。数字字面值,也可以使用 `_` 作为视觉分隔符,使数字更容易读取,例如 `1_000`,其将与咱们指定的 `1000` 相同。 +咱们可以下表 3-2 所示的任何形式,书写整数的字面值。要注意可能是多种数值类型的数字字面值,则允许使用类型后缀来指定类型,比如 `57u8`。数字字面值还可使用 `_` 作为视觉分隔符,使数字更容易读取,例如 `1_000`,其将有着与咱们指定 `1000` 时相同的值。 *表 3-2:Rust 中的数字字面值* | 数字字面值 | 示例 | | :- | :- | -| 十进制(Decimal) | `98_222` | -| 十六进制(Hex) | `0xff` | -| 八进制(Octal) | `0o77` | -| 二进制(Binary) | `0b1111_0000` | -| 字节(仅限 `u8`,Byte(`u8` only)) | `b'A'` | +| 十进制,Decimal | `98_222` | +| 十六进制,Hex | `0xff` | +| 八进制,Octal | `0o77` | +| 二进制,Binary | `0b1111_0000` | +| 字节(仅限 `u8`),Byte(`u8` only) | `b'A'` | -那么,咱们怎么知道,要使用哪种整数类型呢?在不确定时,Rust 默认选项,通常是个很好的开始:整数类型默认为 `i32`。而使用 `isize` 或 `usize` 的主要情况,是在索引某种集合时。 +那么,咱们怎么知道要使用何种整数类型呢?当咱们不确定时,Rust 默认选项通常是个很好的开始:整数类型默认为 `i32`。咱们会用到 `isize` 或 `usize` 的主要情形,是在索引某种集合时。 > 关于 **整数溢出** > -> 假设咱们有个可保存 0 到 255 之间值的 `u8` 类型变量。在咱们尝试将该变量,更改为超出该范围的某个值,比如 256 时,就会发生 *整数溢出,integer overflow*,这可能导致两种行为之一。在咱们以调试模式编译时,Rust 包含了对那些发生时,会引起咱们程序 *中止,panic* 的整数溢出的检查。当某个程序以一个报错而退出时,Rust 便对此使用 *中止,panicking* 一词;我们将在第 9 章,[“使用 `panic!` 的无法恢复错误](../error_handling/panic.md) 小节中,更深入地讨论中止运行。 +> 假设咱们有个可保存 0 到 255 间值的 `u8` 类型的变量。当咱们尝试将该变量修改为某个超出该范围的值,比如 256 时,那么 *整数溢出,integer overflow* 就将发生,其会导致两种行为之一。当咱们以调试模式编译时,Rust 会包含对整数溢出的检查,当这种行为发生时,会导致咱们的程序 *中止,panic*。当程序以某种错误退出时,Rust 会使用 *中止,panicking* 一词;我们将在第 9 章的 [“`panic!` 下的不可恢复错误](../error_handling/panic.md) 小节中,更深入讨论中止运行。 > -> 而使用 `--release` 命令行开关,在发布模式下编译时,Rust 就不会检查会导致中止运行的整数溢出。相反,如果发生溢出,Rust 会执行 *补码回绕,two's complement wrapping*。简而言之,大于该类型所能容纳最大值的值,会 “折回,wrap around” 到该类型所能容纳的最小值。在 `u8` 的情况下,值 256 会变成 0,值 257 会变成 1,依此类推。程序不会中止运行,但变量的值可能不是咱们期望的值。依赖整数溢出的 wrapping 行为,被视为错误。 +> 当咱们以 `--release` 命令行开关在发布模式下编译时,Rust 就 *不* 会包含对导致中止运行的整数溢出的检查。相反,当溢出发生时,Rust 会执行 *二进制补码回绕,two's complement wrapping*。简言之,大于该类型可容纳最大值的值,会 “折回,wrap around” 到该类型可容纳的最小值。在 `u8` 的情况下,值 256 会变成 0,值 257 会变成 1,依此类推。程序将不中止运行,但变量将有着某个可能不是咱们期望他要有的值。依赖于整数溢出的折回行为被视为一种错误。 > -> 要显式地处理溢出的可能性,咱们可以使用标准库为原始数值类型,提供的下面这些方法系列: +> 要显式地处理溢出的可能性,咱们可以使用由标准库针对原始数值类型所提供的下面这些方法族: > -> - 使用 `wrapping_*` 系列方法,如 `wrapping_add`,在所有模式下换行,wrap; -> -> - 使用 `checked_*` 系列方法,在出现溢出时,则返回 `None` 值; -> -> - 使用 `overflowing_*` 系列方法,返回一个值,和一个表明是否存在溢出的布尔值; -> -> - 使用 `saturating_*` 系列方法,对数值的最小值或最大值进行饱和处理。 +> - 以 `wrapping_*` 方法,比如 `wrapping_add`,在所有模式下折回; +> - 以 `checked_*` 方法,在溢出时返回 `None` 值; +> - 以 `overflowing_*` 方法,返回一个值,与一个表明是否溢出的布尔值; +> - 以 `saturating_*` 方法,于最小值或最大值处饱和。 ### 浮点类型 -**Floating-Point Types** - - -Rust 同样有 *浮点数,floating-point numbers*(带有小数点的数)的两种原始类型。Rust 的浮点类型是 `f32` 和 `f64`,大小分别为 `32` 位和 `64` 位。默认类型是 `f64`,因为在现代 CPU 上,其速度与 `f32` 大致相同,但精度更高。所有浮点类型都是带符号的。 +Rust 同样有两种 *浮点数,floating-point numbers* 的两种原始类型,他们属于带有小数点的数字。Rust 的浮点类型为 `f32` 和 `f64`,大小分别是 32 和 64 位。默认类型是 `f64`,因为在现代 CPU 上,其速度与 `f32` 大致相同,但能提供更高精度。两种浮点类型都是有符号的。 下面是个展示浮点数实际操作的示例: @@ -114,6 +113,7 @@ Rust 同样有 *浮点数,floating-point numbers*(带有小数点的数) ```rust fn main() { let x = 2.0; // f64 + let y: f32 = 3.0; // f32 } ``` @@ -123,10 +123,16 @@ fn main() { ### 数值运算 -**Numeric Operations** +Rust 支持咱们所期望的对所有数字类型的基本数学运算: + +- 加法 +- 减法 +- 乘法 +- 除法 +- 及余数(求模) -Rust 支持所有数字类型的基本数学运算:加法、减法、乘法、除法和余数,reminder(求模)。整数的除法,会向零截断到最接近的整数。下面的代码展示了,如何在 let 语句中使用每种数字运算: +整数的除法,会向零方向截断到最接近的整数。以下代码展示了咱们应如何在 let 语句中,使用每种数字运算: 文件名:`src/main.rs` @@ -144,32 +150,28 @@ fn main() { // 除法 let quotient = 56.7 / 32.2; - let floored = 2 / 3; // 结果为 0 + let truncated = -5 / 3; // 结果为 -1 // 余数 - let reminder = 43 % 5; + let remainder = 43 % 5; println! (" 5 + 10 = {sum}, 95.5 - 4.3 = {difference} 4 * 30 = {product} 56.7 / 32.2 = {quotient} - 2 / 3 = {floored} - 43 % 5 = {reminder}"); + -5 / 3 = {truncated} + 43 % 5 = {remainder}"); } ``` -这些语句中的每个表达式,都使用了个数学运算符,并求值为一个单一值,然后绑定到某个变量。[附录 B](../appendix/ops_and_symbols.md) 列出了 Rust 提供的所有运算符。 - +这些语句中的每个表达式,都使用了个数学运算符并求值为单个值,然后其被绑定到某个变量。[附录 B](../appendix/ops_and_symbols.md) 包含 Rust 所提供的全部运算符清单。 ### 布尔值类型 -**The Boolean Type** - - -与大多数其他编程语言中一样,Rust 中的布尔类型,有两个可能的值:`true` 和 `false`。布尔值的大小为一个字节。Rust 中的布尔类型使用 `bool` 指定。例如: +与大多数别的编程语言中一样,Rust 中的布尔类型有两个可能值:`true` 与 `false`。布尔值的大小为一个字节。Rust 中的布尔类型使用 `bool` 指定。例如: 文件名:`src/main.rs` @@ -182,17 +184,12 @@ fn main() { } ``` - -使用布尔值的主要方式,是经由一些条件,例如某个 `if` 表达式。我们将在 [“控制流”](control_flow.md) 小节,介绍 `if` 表达式在 Rust 中的工作原理。 +使用布尔值的主要方式是经由一些条件,例如某个 `if` 表达式。我们将在 [“控制流”](control_flow.md) 小节,介绍 Rust 中 `if` 表达式的工作原理。 ### 字符类型 -**The Character Type** - - -Rust 的 `char` 类型,是这门语言的最原始字母类型。下面是声明出一些 `char` 值的示例: - +Rust 的 `char` 类型,是这门语言的最原始字母类型。下面是声明 `char` 值的一些示例: 文件名:`src/main.rs` @@ -202,29 +199,30 @@ fn main() { let z: char = 'ℤ'; // 带有显式的类型注解 let heart_eyed_cat = '😻'; - println! ("c 为 {c}, z 为 {z}, 爱心猫: {heart_eyed_cat}"); + println! (" + c 为 {c} + z 为 {z} + 爱心猫: {heart_eyed_cat} + "); } ``` -请注意,我们使用单引号指定 `char` 字面值,而字符串字面值则使用双引号。Rust 的 `char` 类型大小为 4 个字节,表示某个 Unicode 的标量值,a Unicode Scalar Value,这意味着他可以表示的字符,不仅仅是 ASCII。在 Rust 中,重音字母、中日韩文字、表情符号和零宽度的空格等,都是有效的 `char` 值。Unicode 标量值的范围为 `U+0000` 至 `U+D7FF` 和 `U+E000` 至 `U+10FFFF`(含)。不过,所谓 “字符”,在 Unicode 中并不是个真正的概念,因此咱们对 “字符” 的直觉,可能与 Rust 中的字符不一致。我们将在第 8 章,[“使用 `String` 存储 UTF-8 编码的文本”](../common_collections/strings.md) 小节中,详细讨论这个问题。 +要注意,我们以单引号指定 `char` 字面值,与使用双引号的字符串字面值相反。Rust 的 `char` 类型大小为 4 字节,表示一个 Unicode 标量值,a Unicode scalar value,这意味着他可表示远超过 ASCII 的字符。重音字母、中日韩文字、表情符号与零宽度空格等等,都属于 Rust 中的有效 `char` 值。Unicode 标量值范围从 `U+0000` 至 `U+D7FF`,及 `U+00E000` 至 `U+10FFFF`(含)。不过,所谓 “字符” 并非真正是个 Unicode 中的概念,因此咱们对 “字符” 的直觉,可能与 Rust 中的 `char` 不一致。我们将在第 8 章的 [“以字符串存储 UTF-8 编码的文本”](../common_collections/strings.md) 小节中,详细讨论这个主题。 ## 复合类型 -**Compound Types** +所谓 *复合类型,compound types*,可将多个值编组为一种类型。Rust 有两种原始复合类型: - -*复合类型,compound types*,可以将多个值编为一种类型。Rust 有两种原始的复合类型:元组和数组。 +- 元组 +- 数组 ### 元组类型 -**The Tuple Type** +所谓元组,是将不同类型的多个值组合在一起,成为一种复合类型的通用方式。元组有着固定的长度:一旦声明,其大小就无法增大或缩小。 - -元组是将不同类型的多个值,组合在一起成为一个复合类型的通用方法。元组有固定的长度:一旦声明了出来,其大小就不能增大或缩小。 - -我们通过在括号内写入一个以逗号分隔的值列表,来创建一个元组。元组中的每个位置都有一个类型,元组中不同值的类型不必相同。在下面这个示例中,我们添加了可选的类型注解: +通过在括号内写下一个逗号分隔的值列表,我们就创建出了个元组。元组中的每个位置都有一种类型,元组中不同值的类型不必相同。在下面这个示例中,我们已添加了可选的类型注解: 文件名:`src/main.rs` @@ -236,7 +234,7 @@ fn main() { ``` -该变量 `tup` 会绑定到整个元组,因为元组被视为单一的复合元素。而要从某个元组中获取单个值,我们可以使用模式匹配,来解构某个元组值,就像这样: +其中变量 `tup` 会绑定到整个元组,因为元组被视为单个复合元素。要获取元组中的单个值,我们可以使用模式匹配解构元组值,就像这样: 文件名:`src/main.rs` @@ -251,10 +249,14 @@ fn main() { } ``` +这个程序首先创建了个元组,并将其绑定到变量 `tup`。其随后以 `let` 使用一个模式,取得 `tup` 并将其转换三个独立变量 `x`、`y` 与 `z`。这称为 *解构,destructuring*,因为他将单个元组分解为三部分。最后,这个程序打印 `y` 的值,即 `6.4`。 -这个程序首先创建了一个元组,并将其绑定到变量 `tup`。然后,程序使用一个带有 `let` 的模式,来取 `tup`,并将其变成三个独立的变量 `x`、`y` 和 `z`。这叫做 *解构,destructuring*,因为他将单个元组,分解为了三个部分。最后,该程序打印出 `y` 的值,即 `6.4`。 -我们还可以通过使用后跟要访问值索引的句点(`.`),直接访问某个元组元素。例如: +> **译注**:这种解构元组时用到的模式匹配,在 Erlang/OTP 中早已存在,且功能更为强大。Erlang/OTP 的模式匹配特性,不仅可以提取元组中的单个元素,甚至可以提取元组的其余部分而得到一个新的元组。 +> +> 参考:[又来模式匹配](https://erlang.xfoss.com/part-ii/Ch03-basic_concepts.html#%E5%8F%88%E6%9D%A5%E6%A8%A1%E5%BC%8F%E5%8C%B9%E9%85%8D) + +我们还可通过使用句点(`.`),后跟我们打算访问值的索引,直接访问元组元素。例如: 文件名:`src/main.rs` @@ -269,24 +271,26 @@ fn main() { let one = x.2; - println! ("x.0:{five_hundred}, x.1:{six_point_four}, x.2:{one}"); + println! (" + x: {:?} + x.0:{five_hundred} + x.1:{six_point_four} + x.2:{one} + ", x) } ``` -此程序创建了一个元组 `x`,然后使用其各自索引,访问了元组中的每个元素。与大多数编程语言一样,元组中的第一个索引为 0。 +这个程序创建了个元组 `x` 并随后使用其各自索引,访问了该元组中的各个元素。与大多数编程语言一样,元组中的第一个索引为 0。 -没有任何值的元组,有一个特殊的名称,*单元值,unit*。这个值及其对应的类型,都被写成 `()`,并表示某个空值,或空返回类型。如果表达式不返回任何别的值,则会隐式返回这个单元值。 +没有任何值的元组有个特殊名字,*单元值,unit*。这个值及其对应的类型都被写成 `()`,并表示一个空值或空的返回类型。当表达式不返回任何别的值时,就会隐式地返回单元值。 ### 数组类型 -**The Array Type** +拥有多个值集合的另一种方式,是在 *数组,array* 下。不同于元组,数组的每个元素都必须有着同一类型。与一些其他语言中的数组不同,Rust 中的数组有着固定长度。 - -另一种拥有多个值集合的方法,是使用 *数组,array*。与元组不同,数组的每个元素,都必须有着同样类型。与其他一些语言中的数组不同,Rust 中的数组有着固定长度。 - -我们将数组中的值,写作方括号内以逗号分隔的列表: +我们要将数组中的值,写作方括号内以逗号分隔的列表: 文件:`src/main.rs` @@ -297,9 +301,9 @@ fn main() { } ``` -当咱们希望咱们的数据,分配在栈上而不是堆上时(我们将在 [第 4 章](ownership/about_ownership.md) 详细讨论栈和堆),或者当咱们希望确保始终有着固定数量元素时,数组就非常有用。不过,数组不如矢量类型灵活。所谓 *矢量,vector*,是标准库提供的一种类似的集合类型,其大小 *可以* 增大或缩小。如果咱们不确定是使用数组还是矢量,那么很可能应该使用矢量。[第 8 章](../common_collections/vectors.md) 会详细讨论矢量。 +当咱们希望咱们数据,如同我们迄今为止所看到的其他类型一样,分配在栈上而不是堆上时(我们将在 [第 4 章](ownership/about_ownership.md) 详细讨论栈和堆),或者当咱们希望确保始终有着固定数量元素时,数组非常有用。不过,数组不如矢量类型灵活。所谓 *矢量,vector*,是由标准库提供的一种类似集合类型,其 *是* 允许在大小(内存用量)上增大或缩小的,因为其内容位于内存堆上。当咱们不确定要使用数组还是矢量时,那么很可能咱们应使用矢量。[第 8 章](../common_collections/vectors.md) 会详细讨论矢量值。 -不过,当我们清楚元素数量今后不会需要改变时,数组会更有用。例如,如果咱们要在程序中使用月份的那些名字时,咱们就可能会使用数组而不是矢量,因为咱们知道他将始终包含 12 个元素: +不过,当我们清楚元素数量无需改变时,数组会更有用。例如,当咱们在程序中用到月份的那些名字时,咱们就可能会使用数组而不是矢量值,因为咱们知道他将始终包含 12 个元素: ```rust @@ -308,7 +312,7 @@ let months = ["January", "February", "March", "April", "May", "June", "July", ``` -咱们可以使用带有每个元素的类型、分号以及数组中元素的数量的方括号,来写出某个数组的类型,如下所示: +咱们会使用带有各个元素类型、分号以及数组中元素数量的方括号,编写数组类型,如下所示: ```rust @@ -316,25 +320,22 @@ let a: [i32; 5] = [-1, 0, 1, 2, 3]; ``` -这里,`i32` 是每个元素的类型。分号后的数字 `5` 表示该数组包含五个元素。 +这里,`i32` 是每个元素的类型。分号后的数字 `5` 表示这个数组包含五个元素。 -咱们还可以通过在方括号中,指定初始值、分号和数组长度,来初始化某个数组,使每个元素都包含相同的值,如下所示: +咱们还可通过在方括号中指定初始值、后跟一个分号及随后该数组的长度,初始化某个数组为每个元素都包含同一个值,如下所示: ```rust let a = [3; 5]; ``` - -名为 `a` 这个数组,将包含 `5` 个元素,都将被初始设置为 `3`。这与写下 `let a = [3, 3, 3, 3, 3]` 相同;但写法更简洁。 +名为 `a` 这个数组将包含 `5` 个都将被初始设置为 `3` 的元素。这与写下 `let a = [3, 3, 3, 3, 3]` 相同;不过以一种更简洁的方式。 -### 访问数组元素 - -**Accessing Array Elements** +### 数组元素的访问 -数组是可以在堆栈上分配的,已知、固定大小的单块内存。咱们可以使用索引,访问数组中的元素,就像这样: +数组是可在栈上分配的已知、固定大小的单个内存块。咱们可以使用索引访问数组的元素,就像这样: 文件名:`src/main.rs` @@ -344,40 +345,40 @@ fn main() { let a = [1, 2, 3, 4, 5]; let first = a[0]; - let last = a[a.len()-1]; + let second = a[1]; - println! ("数组的第一个元素:{first},最后一个元素:{last}"); + println! (" + 数组 a:{:?} + 数组的第一个元素:{first} + 第二个元素:{second} + ", a); } ``` -在此示例中,名为 `first` 的变量将获得值 `1`,因为这是数组中索引 `[0]` 处的值。名为 `last` 的变量将从数组中的索引 `[4]` 处获取到值 `5`。 +在这个示例中,名为 `first` 的变量将得到值 `1`,因为这是该数组中索引 `[0]` 处的值。名为 `second` 的变量得到该数组中的索引 `[1]` 处的值 `2`。 -> **译注**:`println! ("a[0] 为:{a[0]}");` 这种写法,会报出错误。 +> **译注**:`println! ("a[0]: {a[0]}");` 这种写法,会报出错误。 ```console $ cargo build - Compiling tuple_demo v0.1.0 (C:\tools\msys64\home\Lenny.Peng\rust-lang-zh_CN\projects\tuple_demo) -error: invalid format string: expected `'}'`, found `'['` - --> src\main.rs:4:24 - | -4 | println! ("a[0]: {a[0]}"); - | - ^ expected `'}'` in format string - | | - | because of this opening brace - | - = note: if you intended to print `{`, you can escape it using `{{` + Compiling array_demo v0.1.0 (/home/hector/rust-lang-zh_CN/projects/array_demo) +error: invalid format string: expected `}`, found `[` + --> src/main.rs:11:17 + | +11 | a[0]: {a[0]} + | - ^ expected `}` in format string + | | + | because of this opening brace + | + = note: if you intended to print `{`, you can escape it using `{{` -error: could not compile `tuple_demo` (bin "tuple_demo") due to previous error +error: could not compile `array_demo` (bin "array_demo") due to 1 previous error ``` +### 无效数组元素访问 -### 无效的数组元素访问 - -**Invalid Array Element Access** - - -我们来看看,如果咱们尝试访问数组中,超过数组末尾的某个元素,会发生什么。假设咱们要运行下面这段,类似于第 2 章中的猜数游戏,从用户处获取数组索引的代码: +我们来看看当咱们尝试访问某个超过数组末尾的元素时会发生什么。假设咱们运行下面这段,类似于第 2 章中猜数游戏的代码,从用户处获取一个数组索引: 文件名:`src/main.rs` @@ -407,27 +408,23 @@ fn main() { } ``` - -这段代码会编译成功。如果咱们使用 `cargo run` 运行这段代码,并输入 `0`、`1`、`2`、`3` 或 `4`,该程序将打印出数组中,该索引处的相应值。如果咱们输入的数字超过了数组的末尾,例如 `10`,咱们将看到如下输出: - +这段代码会编译成功。当咱们使用 `cargo run` 运行这段代码,并输入 `0`、`1`、`2`、`3` 或 `4` 时,程序将打印出数组中该索引处的相应值。当咱们输入一个超出数组末尾的数字,比如 `10` 时,咱们将看到如下的输出: ```console $ cargo run - Compiling tuple_demo v0.1.0 (C:\tools\msys64\home\Lenny.Peng\rust-lang-zh_CN\projects\tuple_demo) - Finished dev [unoptimized + debuginfo] target(s) in 1.10s - Running `target\debug\tuple_demo.exe` + Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.00s + Running `target/debug/invalid_array_element_access` 请输入一个数组索引。 10 -thread 'main' panicked at src\main.rs:19:19: + +thread 'main' (100331) 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 -error: process didn't exit successfully: `target\debug\tuple_demo.exe` (exit code: 101) ``` +程序在索引操作中使用了某个无效值的时间点,引发了一次运行时错误,a runtime error。程序以一条错误信息退出,且未执行最后的 `println!` 语句。当咱们尝试使用索引访问某个元素时,Rust 将检查咱们指定的索引是否小于数组长度。当索引大于或等于这一长度时,Rust 将终止运行。这一检查必须在运行时进行,尤其在这种情况下,因为编译器不可能知道稍后用户运行代码时,将输入什么值。 -该程序在索引操作中,使用某个无效值时,出现了 *运行时* 错误。程序以一条错误信息退出了,并且没有执行最后的那条 `println!` 语句。当咱们尝试使用索引访问某个元素时,Rust 会检查咱们指定的索引,是否小于数组长度。如果索引大于或等于长度,Rust 就会终止运行。这种检查必须在运行时进行,尤其是在这种情况下,因为编译器不可能知道,某名用户在他们稍后运行代码时,会输入什么值。 - -这是 Rust 内存安全原则,实际应用的一个例子。在许多底层语言中,并无这种检查,而当咱们提供了某个不正确的索引时,无效内存就会被访问到。Rust 通过立即退出,而不是允许这种内存访问并继续,保护咱们免受此类错误的影响。第 9 章将讨论更多的 Rust 错误处理,以及如何编写既不会终止运行,也不允许无效内存访问的,可读安全代码。 +这是 Rust 内存安全原则实际应用的一个例子。在许多底层语言中,这类检查都没有进行,当咱们提供了不正确索引时,无效内存就会被访问。Rust 通过立即退出,而不是允许这一内存访问并继续,保护咱们免受此类错误的影响。第 9 章详细讨论了 Rust 的错误处理,以及咱们怎样可以编写出既不会终止运行,也不允许无效内存访问的可读、安全代码。 (End)