@ -155,22 +139,24 @@ note: `Rectangle` cannot be formatted with the default formatter; try using
`:?` instead if you are using a format string
```
让我们来试试!现在`println!`看起来像`println!("rect1 is {:?}", rect1);`这样。在`{}`中加入`:?`指示符告诉`println!`我们想要使用叫做`Debug`的输出格式。`Debug`是一个 trait,它允许我们在调试代码时以一种对开发者有帮助的方式打印出结构体。
让我们来试试!现在`println!` 宏调用看起来像`println!("rect1 is {:?}", rect1);`这样。在`{}`中加入`:?`指示符告诉`println!`我们想要使用叫做`Debug`的输出格式。`Debug`是一个 trait,它允许我们在调试代码时以一种对开发者有帮助的方式打印出结构体。
让我们试试运行这个变化...见鬼了。仍然能看到一个错误:
让我们试试运行这个变化。见鬼了!仍然能看到一个错误:
```text
error: the trait bound `Rectangle: std::fmt::Debug` is not satisfied
```
虽然编译器又一次给出了一个有帮助的信息!
不过编译器又一次给出了一个有帮助的信息!
```text
note: `Rectangle` cannot be formatted using `:?`; if it is defined in your
crate, add `#[derive(Debug)]` or manually implement it
> 像在 C++ 这样的语言中,有两个不同的运算符来调用方法:`.`直接在对象上调用方法,而`->`在一个对象的指针上调用方法,这时需要先解引用(dereference)指针。换句话说,如果`object`是一个指针,那么`object->something()`就像`(*object).something()`一样。
> 像在 C++ 这样的语言中,有两个不同的运算符来调用方法:`.`直接在对象上调用方法,而`->`在一个对象的指针上调用方法,这时需要先解引用(dereference)指针。换句话说,如果`object`是一个指针,那么`object->something()`就像`(*object).something()`一样。
>
> Rust 并没有一个与`->`等效的运算符;相反,Rust 有一个叫**自动引用和解引用**(*automatic referencing and dereferencing*)的功能。方法调用是 Rust 中少数几个拥有这种行为的地方。
> Rust 并没有一个与`->`等效的运算符;相反,Rust 有一个叫**自动引用和解引用**(*automatic referencing and dereferencing*)的功能。方法调用是 Rust 中少数几个拥有这种行为的地方。
让我们通过一用代码来表现的场景,来看看为什么这里枚举是有用的而且比结构体更合适。比如我们要处理 IP 地址。目前被广泛使用的两个主要 IP 标准:IPv4(version four)和 IPv6(version six)。这是我们的程序只可能会遇到两种 IP 地址:所以可以**枚举**出所有可能的值,这也正是它名字的由来。
让我们通过一用代码来表现的场景,来看看为什么这里枚举是有用的而且比结构体更合适。比如我们要处理 IP 地址。目前被广泛使用的两个主要 IP 标准:IPv4(version four)和 IPv6(version six)。这是我们的程序只可能会遇到两种 IP 地址:所以可以**枚举**出所有可能的值,这也正是它名字的由来。
任何一个 IP 地址要么是 IPv4 的要么是 IPv6 的而不能两者都是。IP 地址的这个特性使得枚举数据结构非常适合这个场景,因为枚举值只可能是其中一个成员。IPv4 和 IPv6 从根本上讲仍是 IP 地址,所以当代码在处理申请任何类型的 IP 地址的场景时应该把他们当作相同的类型。
可以通过在代码中定义一个`IpAddrKind`枚举来表现这个概念并列出可能的 IP 地址类型,`V4`和`V6`。这被称为枚举的**成员**(*variants*):
可以通过在代码中定义一个`IpAddrKind`枚举来表现这个概念并列出可能的 IP 地址类型,`V4`和`V6`。这被称为枚举的**成员**(*variants*):
@ -102,7 +101,7 @@ let loopback = IpAddr::V6(String::from("::1"));
我们直接将数据附加到枚举的每个成员上,这样就不需要一个额外的结构体了。
使用枚举而不是结构体还有另外一个优势:每个成员可以处理不同类型和数量的数据。IPv4 版本的 IP 地址总是含有四个值在 0 和 255 之间的数字部分。如果我们想要将`V4`地址储存为四个`u8`值而`V6`地址仍然表现为一个`String`,这就不能使用结构体了。枚举可以轻易处理的这个情况:
使用枚举而不是结构体还有另外一个优势:每个成员可以处理不同类型和数量的数据。IPv4 版本的 IP 地址总是含有四个值在 0 和 255 之间的数字部分。如果我们想要将`V4`地址储存为四个`u8`值而`V6`地址仍然表现为一个`String`,这就不能使用结构体了。枚举可以轻易处理的这个情况:
```rust
enum IpAddr {
@ -115,7 +114,7 @@ let home = IpAddr::V4(127, 0, 0, 1);
let loopback = IpAddr::V6(String::from("::1"));
```
这些代码展示了使用枚举来储存两种不同 IP 地址的几种可能的选择。然而,事实证明储存和编码 IP 地址实在是太常见了[以致标准库提供了一个可供使用的定义!][IpAddr]<!-- ignore -->让我们看看标准库如何定义`IpAddr`的:它正有着跟我们定义和使用的一样的枚举和成员,不过它将成员中的地址数据嵌入到了两个不同形式的结构体中,他们对不同的成员的定义是不同的:
这些代码展示了使用枚举来储存两种不同 IP 地址的几种可能的选择。然而,事实证明储存和编码 IP 地址实在是太常见了[以致标准库提供了一个可供使用的定义!][IpAddr]<!-- ignore -->让我们看看标准库如何定义`IpAddr`的:它正有着跟我们定义和使用的一样的枚举和成员,不过它将成员中的地址数据嵌入到了两个不同形式的结构体中,他们对不同的成员的定义是不同的: