From 9a35a7310fcbb999ab09bc98450ddae7d317e3f3 Mon Sep 17 00:00:00 2001 From: "Peng Hailin," Date: Sun, 17 Dec 2023 20:13:54 +0800 Subject: [PATCH] Refining Ch04. --- .../defining_an_enum.md | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/enums_and_pattern_matching/defining_an_enum.md b/src/enums_and_pattern_matching/defining_an_enum.md index 2214e85..1483241 100644 --- a/src/enums_and_pattern_matching/defining_an_enum.md +++ b/src/enums_and_pattern_matching/defining_an_enum.md @@ -79,13 +79,12 @@ fn main() { } ``` -*清单 6-1:使用结构体 `struct` 存储 IP 地址的数据及 `IpAddrKind` 变种* +*清单 6-1:使用 `struct` 存储 IP 地址的数据及 `IpAddrKind` 变种* +这里,我们定义了个有着两个字段的结构体 `IpAddr`:一个是 `IpAddrKind` 类型的 `kind` 字段(我们之前定义的枚举),另一个是 `String` 类型的 `address` 字段。咱们有着此结构体的两个实例。第一个实例是 `home`,其 `kind` 字段的值 `IpAddrKind::V4`,与其关联的地址数据为 `127.0.0.1`。第二个实例是 `loopback`。其 `kind` 字段值为 `IpAddrKind` 的另一变种 `V6`,关联的地址是 `::1`。我们已使用了一个结构体,将 `kind` 和 `address` 两个值捆绑在一起,因此现在这个变种,与值相关联了。 +不过,只使用一个枚举,来表示这同样的概念,更为简洁:我们可以将数据直接放入各个枚举变种中,而不是将枚举放在结构体中。下面这个 `IpAddr` 枚举的新定义指出,`V4` 和 `V6` 两个变种,都将有着关联的 `String` 值: -这里已定义了有着两个字段的结构体 `IpAddr`:一个类型为 `IpAddrKind` (即先前定义的那个枚举)的 `kind` 字段,以及一个类型为 `String` 的 `address` 字段。这里有该结构体的两个实例。第一个是 `home`,而他有着与地址数据 `127.0.0.1` 关联的 `IpAddrKind::V4` 作为其 `kind` 的值。第二个实例为 `loopback`。这个实例则有不同的 `IpAddrKind` 变种作为其 `kind` 的值,即 `V6`,与 `kind` 关联的是地址 `::1`。由于这里使用了结构体将 `kind` 与 `address` 值捆绑在一起,因此现在这个 `IpAddrKind` 的变种就与那个 `String` 值关联起来了。 - -不过,仅使用一个枚举来表示这同一概念,就会更加简练:与其将枚举放在结构体内部,可将数据直接放在各个枚举变种里头。那么这新的 `IpAddr` 枚举定义,就是说 `V4` 与 `V6` 两个变种,将同时有着关联的 `String` 值: ```rust enum IpAddr { @@ -93,17 +92,16 @@ enum IpAddr { V6(String), } -fn main() { - let home = IpAddr::V4(String::from("127.0.0.1")); let loopback = IpAddr::V6(String::from("::1")); -} ``` -这里把数据直接附加到枚举的各个变种上,因此就无需额外的结构体了。这里还更易于发现枚举工作原理的另一细节:所定义的各个枚举变种的名字,还成为了构造该枚举实例的函数。那就是说,`IpAddr::V4()` 现在是个取 `String` 参数并返回该 `IpAddr` 类型实例的函数调用了。作为定义枚举的结果,这里让这个构造函数自动就定义好了。 -这里还有另一个使用枚举而非结构体的好处:各个变种可以有不同类型及数量的关联数据。版本四类型的 IP 地址,将始终有着四个会有着 `0` 到 `255` 之间值的数字部分。在希望将 `V4` 地址存储为四个 `u8` 值,而仍然将 `V6` 地址表示为一个 `String` 值时,那就没法用结构体了,而枚举则能轻易处理这样的情况: +我们直接将数据,附加到枚举的各个变种,因此不需要一个额外结构体。在这里,我们还可以更容易地了解,枚举工作原理的另一细节:我们定义的每个枚举变种名字,还成为了用来构造枚举实例的一个函数。也就是说,`IpAddr::V4()` 是个取一个 `String` 参数,并返回 `IpAddr` 类型实例的函数调用。定义那个枚举时,我们就自动获得了这个构造函数。 + +使用枚举而非结构体,还有另一好处:每个变种都可以有不同类型和数量的关联数据。版本四的 IP 地址,总是有范围在 0 到 255 之间的四个数字部分。如果我们打算将 `V4` 地址,存储为四个 `u8` 值,但仍想要将 `V6` 地址,表示为一个 `String` 值,那么我们就无法使用结构体。枚举则可以轻松处理这种情况: + ```rust enum IpAddr { @@ -111,22 +109,21 @@ enum IpAddr { V6(String), } -fn main() { - let home = IpAddr::V4(127, 0, 0, 1); let loopback = IpAddr::V6(String::from("::1")); -} ``` -到这里,就已经给出了好几种定义用于存储版本四和版本六 IP 地址的数据结构了。然而事实表明,想要存储 IP 地址,及对这些 IP 地址所属类别进行编码是如此普遍,以致 [标准库就有一个可加以使用的定义](https://doc.rust-lang.org/std/net/enum.IpAddr.html)!下面就来看看,标准库是怎样定义 `IpAddr` 的:他有着与这里曾定义和使用过的相同枚举和变种,不过标准库是将地址数据,以两个不同结构体的形式,嵌入到变种里的,对两个枚举变种,定义了不同的结构体。 + +我们已经展示了几种不同的,定义存储第四和第六版 IP 地址数据结构的方法。然而,事实证明,想要存储 IP 地址并对其进行编码,是如此普遍,以致 [标准库就有我们可以使用的定义](https://doc.rust-lang.org/std/net/enum.IpAddr.html)!我们来看看标准库是如何定义 `IpAddr` 的:他有着与我们定义和使用的完全同样的枚举和变种,但他将地址数据,以两个不同结构体的形式,嵌入到两个变种中,对于每个变种,表示地址数据的结构体定义都不同: + ```rust struct Ipv4Addr { // --跳过-- } -struct Ipv4Addr { +struct Ipv6Addr { // --跳过-- } @@ -136,11 +133,13 @@ enum IpAddr { } ``` -这段代码说明可将任何类别的数据放在枚举变种里面:比如字符串、数字类型,或结构体等等。甚至可以包含另一枚举!还说明了,标准库类型,通常也并不比咱们自己编写的代码复杂多少。 -请注意,由于这里不曾将标准库的 `IpAddr` 定义带入到这里的作用域,因此即使标准库包含了一个 `IpAddr` 的定义,这里也仍然可以毫无冲突地创建与使用自己的 `IpAddr` 定义。在第 7 章就会讲到有关带入类型到作用域的问题。 +这段代码说明,咱们可以在枚举变种中,放入任何类别的数据:比如字符串、数字类型或结构体等。咱们甚至还可以包含另一枚举!此外,一些标准库类型,通常也不会比咱们接下来会构造出的类型复杂多少。 + +请注意,即使标准库包含了 `IpAddr` 的定义,我们仍然可以创建并使用咱们自己的定义,而不会发生冲突,因为我们还没有将标准库的定义,引入我们的作用域。我们将在第 7 章中,详细讨论将类型引入作用域的问题。 + +我们再来看看,下面清单 6-2 中枚举的另一示例:这个枚举的变种中,嵌入了多种类型。 -来看看下面清单 6-2 中另一个枚举的示例:这个枚举有着嵌入到其各个变种中的种类繁多的类型。 ```rust enum Message { @@ -153,13 +152,18 @@ enum Message { *清单 6-2:每个变种都存储了不同数量和类型值的 `Message` 枚举* -这个枚举有着四个带有不同类型数据的变种: + +该枚举有着分别嵌入了不同类型的四个变种: - `Quit` 变种完全没有与其关联的数据; + - `Move` 变种像结构体一样,有着两个命名的字段; + - `Write` 变种包含了单个 `String`; + - `ChangeColor` 编程包含了三个 `i32` 的值。 + 定义一个有着一些如上面清单 6-2 中变种的枚举,与定义不同种类的结构体定义类似,不同在于枚举未使用关键字 `struct`,且所有变种在 `Message` 类型下组织在了一起。下面这些结构体,就可保存之前各个枚举变种所保存的那些同样数据: ```rust