|
|
|
@ -1,8 +1,7 @@
|
|
|
|
|
## 结构体的定义和实例化
|
|
|
|
|
|
|
|
|
|
> [ch05-01-defining-structs.md](https://github.com/rust-lang/book/blob/main/src/ch05-01-defining-structs.md)
|
|
|
|
|
> <br>
|
|
|
|
|
> commit a371f82b0916cf21de2d56bd386ca5d72f7699b0
|
|
|
|
|
<!-- https://github.com/rust-lang/book/blob/main/src/ch05-01-defining-structs.md -->
|
|
|
|
|
<!-- commit e4681b573677380154825f383546b68e6111a725 -->
|
|
|
|
|
|
|
|
|
|
结构体和我们在[“元组类型”][tuples]部分论过的元组类似,它们都包含多个相关的值。和元组一样,结构体的每一部分可以是不同类型。但不同于元组,结构体需要命名各部分数据以便能清楚的表明其值的意义。由于有了这些名字,结构体比元组更灵活:不需要依赖顺序来指定或访问实例中的值。
|
|
|
|
|
|
|
|
|
@ -90,11 +89,11 @@
|
|
|
|
|
|
|
|
|
|
示例 5-7 中的代码也在 `user2` 中创建了一个新实例,但该实例中 `email` 字段的值与 `user1` 不同,而 `username`、 `active` 和 `sign_in_count` 字段的值与 `user1` 相同。`..user1` 必须放在最后,以指定其余的字段应从 `user1` 的相应字段中获取其值,但我们可以选择以任何顺序为任意字段指定值,而不用考虑结构体定义中字段的顺序。
|
|
|
|
|
|
|
|
|
|
请注意,结构更新语法就像带有 `=` 的赋值,因为它移动了数据,就像我们在[“使用移动的变量与数据交互”][move]部分讲到的一样。在这个例子中,总体上说我们在创建 `user2` 后就不能再使用 `user1` 了,因为 `user1` 的 `username` 字段中的 `String` 被移到 `user2` 中。如果我们给 `user2` 的 `email` 和 `username` 都赋予新的 `String` 值,从而只使用 `user1` 的 `active` 和 `sign_in_count` 值,那么 `user1` 在创建 `user2` 后仍然有效。`active` 和 `sign_in_count` 的类型是实现 `Copy` trait 的类型,所以我们在[“使用克隆的变量与数据交互”][copy] 部分讨论的行为同样适用。
|
|
|
|
|
请注意,结构更新语法就像带有 `=` 的赋值,因为它移动了数据,就像我们在[“使用移动的变量与数据交互”][move]部分讲到的一样。在这个例子中,总体上说我们在创建 `user2` 后就不能再使用 `user1` 了,因为 `user1` 的 `username` 字段中的 `String` 被移到 `user2` 中。如果我们给 `user2` 的 `email` 和 `username` 都赋予新的 `String` 值,从而只复用 `user1` 的 `active` 和 `sign_in_count` 值,那么 `user1` 在创建 `user2` 后仍然有效。`active` 和 `sign_in_count` 的类型是实现 `Copy` trait 的类型,所以我们在[“使用克隆的变量与数据交互”][copy] 部分讨论的行为同样适用。在本例中我们也可以继续使用 `user1.email`,因为它的值并未从 `user1` 中移动出去。
|
|
|
|
|
|
|
|
|
|
### 使用没有命名字段的元组结构体来创建不同的类型
|
|
|
|
|
|
|
|
|
|
也可以定义与元组(在第三章讨论过)类似的结构体,称为 **元组结构体**(*tuple structs*)。元组结构体有着结构体名称提供的含义,但没有具体的字段名,只有字段的类型。当你想给整个元组取一个名字,并使元组成为与其他元组不同的类型时,元组结构体是很有用的,这时像常规结构体那样为每个字段命名就显得多余和形式化了。
|
|
|
|
|
也可以定义与元组类似的结构体,称为 **元组结构体**(*tuple structs*)。元组结构体有着结构体名称提供的含义,但没有具体的字段名,只有字段的类型。当你想给整个元组取一个名字,并使元组成为与其他元组不同的类型时,元组结构体是很有用的,这时像常规结构体那样为每个字段命名就显得多余和形式化了。
|
|
|
|
|
|
|
|
|
|
要定义元组结构体,以 `struct` 关键字和结构体名开头并后跟元组中的类型。例如,下面是两个分别叫做 `Color` 和 `Point` 元组结构体的定义和用法:
|
|
|
|
|
|
|
|
|
@ -104,11 +103,11 @@
|
|
|
|
|
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/no-listing-01-tuple-structs/src/main.rs}}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
注意 `black` 和 `origin` 值的类型不同,因为它们是不同的元组结构体的实例。你定义的每一个结构体有其自己的类型,即使结构体中的字段可能有着相同的类型。例如,一个获取 `Color` 类型参数的函数不能接受 `Point` 作为参数,即便这两个类型都由三个 `i32` 值组成。在其他方面,元组结构体实例类似于元组,你可以将它们解构为单独的部分,也可以使用 `.` 后跟索引来访问单独的值,等等。
|
|
|
|
|
注意 `black` 和 `origin` 值的类型不同,因为它们是不同的元组结构体的实例。你定义的每一个结构体有其自己的类型,即使结构体中的字段可能有着相同的类型。例如,一个获取 `Color` 类型参数的函数不能接受 `Point` 作为参数,即便这两个类型都由三个 `i32` 值组成。除此之外,元组结构体实例类似于元组,你可以将它们解构为单独的部分,也可以使用 `.` 后跟索引来访问单独的值。与元组不同的是,解构元组结构体时必须写明结构体的类型。例如,我们可以写 `let Point(x, y, z) = origin;`,将 `origin` 的值解构到名为 `x`、`y` 和 `z` 的变量中。
|
|
|
|
|
|
|
|
|
|
### 没有任何字段的类单元结构体
|
|
|
|
|
|
|
|
|
|
我们也可以定义一个没有任何字段的结构体!它们被称为 **类单元结构体**(*unit-like structs*)因为它们类似于 `()`,即[“元组类型”][tuples]一节中提到的 unit 类型。类单元结构体常常在你想要在某个类型上实现 trait 但不需要在类型中存储数据的时候发挥作用。我们将在第十章介绍 trait。下面是一个声明和实例化一个名为 `AlwaysEqual` 的 unit 结构的例子。
|
|
|
|
|
我们也可以定义一个没有任何字段的结构体!它们被称为 **类单元结构体**(*unit-like structs*)因为它们类似于 `()`,即[“元组类型”][tuples]一节中提到的 unit 类型。类单元结构体常常在你想要在某个类型上实现 trait 但不需要在类型中存储数据的时候发挥作用。我们将在第十章介绍 trait。下面是一个声明和实例化一个名为 `AlwaysEqual` 的 unit 结构的示例:
|
|
|
|
|
|
|
|
|
|
<span class="filename">文件名:src/main.rs</span>
|
|
|
|
|
|
|
|
|
@ -116,13 +115,13 @@
|
|
|
|
|
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/no-listing-04-unit-like-structs/src/main.rs}}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
为了定义 `AlwaysEqual`,我们使用 `struct` 关键字,接着是我们想要的名称,然后是一个分号。不需要花括号或圆括号!然后,我们可以以类似的方式在 `subject` 变量中创建 `AlwaysEqual` 的实例:只需使用我们定义的名称,无需任何花括号或圆括号。设想我们稍后将为这个类型实现某种行为,使得每个 `AlwaysEqual` 的实例始终等于任何其它类型的实例,也许是为了获得一个已知的结果以便进行测试。我们不需要任何数据来实现这种行为!在第十章中,你会看到如何定义特征并在任何类型上实现它们,包括类单元结构体。
|
|
|
|
|
为了定义 `AlwaysEqual`,我们使用 `struct` 关键字,接着是我们想要的名称,然后是一个分号。不需要花括号或圆括号!然后,我们可以以类似的方式在 `subject` 变量中创建 `AlwaysEqual` 的实例:只需使用我们定义的名称,无需任何花括号或圆括号。设想我们稍后将为这个类型实现某种行为,使得每个 `AlwaysEqual` 的实例始终等于任何其它类型的实例,也许是为了获得一个已知的结果以便进行测试。我们不需要任何数据来实现这种行为!在第十章中,你会看到如何定义 trait 并在任何类型上实现它们,包括类单元结构体。
|
|
|
|
|
|
|
|
|
|
> ### 结构体数据的所有权
|
|
|
|
|
>
|
|
|
|
|
> 在示例 5-1 中的 `User` 结构体的定义中,我们使用了自身拥有所有权的 `String` 类型而不是 `&str` 字符串 slice 类型。这是一个有意而为之的选择,因为我们想要这个结构体拥有它所有的数据,为此只要整个结构体是有效的话其数据也是有效的。
|
|
|
|
|
>
|
|
|
|
|
> 可以使结构体存储被其他对象拥有的数据的引用,不过这么做的话需要用上 **生命周期**(*lifetimes*),这是一个第十章会讨论的 Rust 功能。生命周期确保结构体引用的数据有效性跟结构体本身保持一致。如果你尝试在结构体中存储一个引用而不指定生命周期将是无效的,比如这样:
|
|
|
|
|
> 可以使结构体存储被其他对象拥有的数据的引用,不过这么做的话需要用上 **生命周期**(*lifetimes*),这是一个第十章会讨论的 Rust 特性。生命周期确保结构体引用的数据有效性跟结构体本身保持一致。如果你尝试在结构体中存储一个引用而不指定生命周期将是无效的,比如这样:
|
|
|
|
|
>
|
|
|
|
|
> <span class="filename">文件名:src/main.rs</span>
|
|
|
|
|
>
|
|
|
|
@ -177,7 +176,7 @@
|
|
|
|
|
> |
|
|
|
|
|
>
|
|
|
|
|
> For more information about this error, try `rustc --explain E0106`.
|
|
|
|
|
> error: could not compile `structs` due to 2 previous errors
|
|
|
|
|
> error: could not compile `structs` (bin "structs") due to 2 previous errors
|
|
|
|
|
> ```
|
|
|
|
|
>
|
|
|
|
|
> 第十章会讲到如何修复这个问题以便在结构体中存储引用,不过现在,我们会使用像 `String` 这类拥有所有权的类型来替代 `&str` 这样的引用以修正这个错误。
|
|
|
|
|