Update ch05

This commit is contained in:
Aaran Xu 2022-01-30 14:09:43 +08:00
parent 825bb7bb77
commit 3ff05f558b
47 changed files with 282 additions and 383 deletions

View File

@ -1,7 +1,6 @@
[package]
name = "structs"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
edition = "2021"
[dependencies]

View File

@ -1,9 +1,9 @@
// ANCHOR: here
struct User {
active: bool,
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
// ANCHOR_END: here

View File

@ -1,7 +1,6 @@
[package]
name = "structs"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
edition = "2021"
[dependencies]

View File

@ -1,17 +1,17 @@
struct User {
active: bool,
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
// ANCHOR: here
fn main() {
// ANCHOR: here
let user1 = User {
email: String::from("someone@example.com"),
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};
// ANCHOR_END: here
}
// ANCHOR_END: here

View File

@ -1,7 +1,6 @@
[package]
name = "structs"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
edition = "2021"
[dependencies]

View File

@ -1,12 +1,12 @@
struct User {
active: bool,
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
// ANCHOR: here
fn main() {
// ANCHOR: here
let mut user1 = User {
email: String::from("someone@example.com"),
username: String::from("someusername123"),
@ -15,5 +15,5 @@ fn main() {
};
user1.email = String::from("anotheremail@example.com");
// ANCHOR_END: here
}
// ANCHOR_END: here

View File

@ -1,7 +1,6 @@
[package]
name = "structs"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
edition = "2021"
[dependencies]

View File

@ -1,8 +1,8 @@
struct User {
active: bool,
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
// ANCHOR: here

View File

@ -1,7 +1,6 @@
[package]
name = "structs"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
edition = "2021"
[dependencies]

View File

@ -1,8 +1,8 @@
struct User {
active: bool,
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
// ANCHOR: here

View File

@ -1,7 +1,6 @@
[package]
name = "structs"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
edition = "2021"
[dependencies]

View File

@ -1,24 +1,28 @@
struct User {
active: bool,
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
// ANCHOR: here
fn main() {
// --snip--
// ANCHOR_END: here
let user1 = User {
email: String::from("someone@example.com"),
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};
// ANCHOR: here
let user2 = User {
email: String::from("another@example.com"),
username: String::from("anotherusername567"),
active: user1.active,
username: user1.username,
email: String::from("another@example.com"),
sign_in_count: user1.sign_in_count,
};
// ANCHOR_END: here
}
// ANCHOR_END: here

View File

@ -1,7 +1,6 @@
[package]
name = "structs"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
edition = "2021"
[dependencies]

View File

@ -1,23 +1,26 @@
struct User {
active: bool,
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
// ANCHOR: here
fn main() {
// --snip--
// ANCHOR_END: here
let user1 = User {
email: String::from("someone@example.com"),
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};
// ANCHOR: here
let user2 = User {
email: String::from("another@example.com"),
username: String::from("anotherusername567"),
..user1
};
// ANCHOR_END: here
}
// ANCHOR_END: here

View File

@ -1,7 +1,6 @@
[package]
name = "rectangles"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
edition = "2021"
[dependencies]

View File

@ -1,7 +1,6 @@
[package]
name = "rectangles"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
edition = "2021"
[dependencies]

View File

@ -1,7 +1,6 @@
[package]
name = "rectangles"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
edition = "2021"
[dependencies]

View File

@ -1,7 +1,6 @@
[package]
name = "rectangles"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
edition = "2021"
[dependencies]

View File

@ -8,12 +8,7 @@ error[E0277]: `Rectangle` doesn't implement `std::fmt::Display`
|
= help: the trait `std::fmt::Display` is not implemented for `Rectangle`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
= note: required by `std::fmt::Display::fmt`
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to previous error
= note: this error originates in the macro `$crate::format_args_nl` (in Nightly builds, run with -Z macro-backtrace for more info)
For more information about this error, try `rustc --explain E0277`.
error: could not compile `rectangles`
To learn more, run the command again with --verbose.
error: could not compile `rectangles` due to previous error

View File

@ -1,7 +1,6 @@
[package]
name = "rectangles"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
edition = "2021"
[dependencies]

View File

@ -1,7 +1,6 @@
[package]
name = "rectangles"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
edition = "2021"
[dependencies]

View File

@ -1,7 +1,6 @@
[package]
name = "rectangles"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
edition = "2021"
[dependencies]

View File

@ -1,7 +1,6 @@
[package]
name = "rectangles"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
edition = "2021"
[dependencies]

View File

@ -1,7 +1,6 @@
[package]
name = "rectangles"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
edition = "2021"
[dependencies]

View File

@ -1,7 +1,6 @@
[package]
name = "structs"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
edition = "2021"
[dependencies]

View File

@ -1,9 +1,7 @@
fn main() {
// ANCHOR: here
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
fn main() {
let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);
// ANCHOR_END: here
}

View File

@ -1,8 +1,7 @@
[package]
name = "structs"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -1,33 +1,31 @@
$ cargo run
Compiling structs v0.1.0 (file:///projects/structs)
error[E0106]: missing lifetime specifier
--> src/main.rs:2:15
--> src/main.rs:3:15
|
2 | username: &str,
3 | username: &str,
| ^ expected named lifetime parameter
|
help: consider introducing a named lifetime parameter
|
1 | struct User<'a> {
2 | username: &'a str,
1 ~ struct User<'a> {
2 | active: bool,
3 ~ username: &'a str,
|
error[E0106]: missing lifetime specifier
--> src/main.rs:3:12
--> src/main.rs:4:12
|
3 | email: &str,
4 | email: &str,
| ^ expected named lifetime parameter
|
help: consider introducing a named lifetime parameter
|
1 | struct User<'a> {
2 | username: &str,
3 | email: &'a str,
1 ~ struct User<'a> {
2 | active: bool,
3 | username: &str,
4 ~ email: &'a str,
|
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0106`.
error: could not compile `structs`
To learn more, run the command again with --verbose.
error: could not compile `structs` due to 2 previous errors

View File

@ -1,8 +1,8 @@
struct User {
active: bool,
username: &str,
email: &str,
sign_in_count: u64,
active: bool,
}
fn main() {

View File

@ -1,7 +1,6 @@
[package]
name = "rectangles"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
edition = "2021"
[dependencies]

View File

@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "structs"
version = "0.1.0"

View File

@ -0,0 +1,6 @@
[package]
name = "structs"
version = "0.1.0"
edition = "2021"
[dependencies]

View File

@ -0,0 +1,5 @@
struct AlwaysEqual;
fn main() {
let subject = AlwaysEqual;
}

View File

@ -0,0 +1,5 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "rectangles"
version = "0.1.0"

View File

@ -0,0 +1,6 @@
[package]
name = "rectangles"
version = "0.1.0"
edition = "2021"
[dependencies]

View File

@ -0,0 +1,9 @@
$ cargo run
Compiling rectangles v0.1.0 (file:///projects/rectangles)
Finished dev [unoptimized + debuginfo] target(s) in 0.61s
Running `target/debug/rectangles`
[src/main.rs:10] 30 * scale = 60
[src/main.rs:14] &rect1 = Rectangle {
width: 60,
height: 50,
}

View File

@ -0,0 +1,15 @@
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let scale = 2;
let rect1 = Rectangle {
width: dbg!(30 * scale),
height: 50,
};
dbg!(&rect1);
}

View File

@ -0,0 +1,5 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "rectangles"
version = "0.1.0"

View File

@ -0,0 +1,6 @@
[package]
name = "rectangles"
version = "0.1.0"
edition = "2021"
[dependencies]

View File

@ -0,0 +1,24 @@
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
// ANCHOR: here
impl Rectangle {
fn width(&self) -> bool {
self.width > 0
}
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
if rect1.width() {
println!("The rectangle has a nonzero width; it is {}", rect1.width);
}
}
// ANCHOR_END: here

View File

@ -1,7 +1,6 @@
[package]
name = "rectangles"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
edition = "2021"
[dependencies]

View File

@ -7,13 +7,8 @@ error[E0277]: `Rectangle` doesn't implement `Debug`
| ^^^^^ `Rectangle` cannot be formatted using `{:?}`
|
= help: the trait `Debug` is not implemented for `Rectangle`
= note: add `#[derive(Debug)]` or manually implement `Debug`
= note: required by `std::fmt::Debug::fmt`
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to previous error
= note: add `#[derive(Debug)]` to `Rectangle` or manually `impl Debug for Rectangle`
= note: this error originates in the macro `$crate::format_args_nl` (in Nightly builds, run with -Z macro-backtrace for more info)
For more information about this error, try `rustc --explain E0277`.
error: could not compile `rectangles`
To learn more, run the command again with --verbose.
error: could not compile `rectangles` due to previous error

View File

@ -1,7 +1,6 @@
[package]
name = "rectangles"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
edition = "2021"
[dependencies]

View File

@ -2,3 +2,4 @@
*struct*,或者 *structure*,是一个自定义数据类型,允许你命名和包装多个相关的值,从而形成一个有意义的组合。如果你熟悉一门面向对象语言,*struct* 就像对象中的数据属性。在本章中,我们会对比元组与结构体的异同,演示结构体的用法,并讨论如何在结构体上定义方法和关联函数来指定与结构体数据相关的行为。你可以在程序中基于结构体和枚举(*enum*)(在第 6 章介绍)创建新类型,以充分利用 Rust 的编译时类型检查。
*struct*,或者 *structure*,是一个自定义数据类型,允许你命名和包装多个相关的值,从而形成一个有意义的组合。如果你熟悉一门面向对象语言,*struct* 就像对象中的数据属性。在本章中,我们会对元组和结构体进行比较和对比,以及演示如何定义和实例化结构体,并讨论如何定义关联函数,特别是被称为**方法**的那种关联函数,以指定与结构体类型相关的行为。你可以在程序中基于结构体和枚举(*enum*)(在第 6 章介绍)创建新类型,以充分利用 Rust 的编译时类型检查。

View File

@ -1,36 +1,19 @@
## 定义并实例化结构体
结构体和我们在第 3 章讨论过的元组类似。和元组一样,结构体的每一部分可以是不同类型。但不同于元组,结构体需要命名各部分数据以便能清楚的表明其值的意义。由于有了这些名字,结构体比元组更灵活:不需要依赖顺序来指定或访问实例中的值。
结构体和我们在[“元组类型”][tuples]<!-- ignore -->章节论过的元组类似。和元组一样,结构体的每一部分可以是不同类型。但不同于元组,结构体需要命名各部分数据以便能清楚的表明其值的意义。由于有了这些名字,结构体比元组更灵活:不需要依赖顺序来指定或访问实例中的值。
定义结构体,需要使用 `struct` 关键字并为整个结构体提供一个名字。结构体的名字需要描述它所组合的数据的意义。接着,在大括号中,定义每一部分数据的名字和类型,我们称为 **字段***field*)。例如,示例 5-1 展示了一个存储用户账号信息的结构体:
```rust
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-01/src/main.rs:here}}
```
<span class="caption">示例 5-1`User` 结构体定义</span>
一旦定义了结构体后,为了使用它,通过为每个字段指定具体值来创建这个结构体的 **实例**。创建一个实例需要以结构体的名字开头,接着在大括号中使用 `key: value` 键-值对的形式提供字段,其中 key 是字段的名字value 是需要存储在字段中的数据值。实例中字段的顺序不需要和它们在结构体中声明的顺序一致。换句话说,结构体的定义就像一个类型的通用模板,而实例则会在这个模板中放入特定数据来创建这个类型的值。例如,可以像示例 5-2 这样来声明一个特定的用户:
一旦定义了结构体后,为了使用它,通过为每个字段指定具体值来创建这个结构体的**实例**。创建一个实例需要以结构体的名字开头,接着在大括号中使用 `key: value` 键-值对的形式提供字段,其中 key 是字段的名字value 是需要存储在字段中的数据值。实例中字段的顺序不需要和它们在结构体中声明的顺序一致。换句话说,结构体的定义就像一个类型的通用模板,而实例则会在这个模板中放入特定数据来创建这个类型的值。例如,可以像示例 5-2 这样来声明一个特定的用户:
```rust
# struct User {
# username: String,
# email: String,
# sign_in_count: u64,
# active: bool,
# }
#
let user1 = User {
email: String::from("someone@example.com"),
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-02/src/main.rs:here}}
```
<span class="caption">示例 5-2创建 `User` 结构体的实例</span>
@ -38,21 +21,7 @@ let user1 = User {
为了从结构体中获取某个特定的值,可以使用点号。如果我们只想要用户的邮箱地址,可以用 `user1.email`。要更改结构体中的值,如果结构体的实例是可变的,我们可以使用点号并为对应的字段赋值。示例 5-3 展示了如何改变一个可变的 `User` 实例 `email` 字段的值:
```rust
# struct User {
# username: String,
# email: String,
# sign_in_count: u64,
# active: bool,
# }
#
let mut user1 = User {
email: String::from("someone@example.com"),
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};
user1.email = String::from("anotheremail@example.com");
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-03/src/main.rs:here}}
```
<span class="caption">示例 5-3改变 `User` 实例 `email` 字段的值</span>
@ -62,21 +31,7 @@ user1.email = String::from("anotheremail@example.com");
示例 5-4 显示了一个 `build_user` 函数,它返回一个带有给定的 email 和用户名的 `User` 结构体实例。`active` 字段的值为 `true`,并且 `sign_in_count` 的值为 `1`
```rust
# struct User {
# username: String,
# email: String,
# sign_in_count: u64,
# active: bool,
# }
#
fn build_user(email: String, username: String) -> User {
User {
email: email,
username: username,
active: true,
sign_in_count: 1,
}
}
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-04/src/main.rs:here}}
```
<span class="caption">示例 5-4`build_user` 函数获取 email 和用户名并返回 `User` 实例</span>
@ -88,21 +43,7 @@ fn build_user(email: String, username: String) -> User {
因为示例 5-4 中的参数名与字段名都完全相同,我们可以使用 **字段初始化简写语法***field init shorthand*)来重写 `build_user`,这样其行为与之前完全相同,不过无需重复 `email``username` 了,如示例 5-5 所示。
```rust
# struct User {
# username: String,
# email: String,
# sign_in_count: u64,
# active: bool,
# }
#
fn build_user(email: String, username: String) -> User {
User {
email,
username,
active: true,
sign_in_count: 1,
}
}
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-05/src/main.rs:here}}
```
<span class="caption">示例 5-5`build_user` 函数使用了字段初始化简写语法,因为 `email``username` 参数与结构体字段同名</span>
@ -111,97 +52,66 @@ fn build_user(email: String, username: String) -> User {
### 使用结构体更新语法从其他实例创建实例
使用旧实例的大部分值但改变其部分值来创建一个新的结构体实例通常是很有帮助的。这可以通过 **结构体更新语法***struct update syntax*)实现。
使用旧实例的大部分值但改变其部分值来创建一个新的结构体实例通常很有用。这可以通过**结构体更新语法***struct update syntax*)实现。
首先,示例 5-6 展示了不使用更新语法时,如何在 `user2` 中创建一个新 `User` 实例。我们为 `email``username` 设置了新的值,其他值则使用了实例 5-2 中创建的 `user1` 中的同名值:
```rust
# struct User {
# username: String,
# email: String,
# sign_in_count: u64,
# active: bool,
# }
#
# let user1 = User {
# email: String::from("someone@example.com"),
# username: String::from("someusername123"),
# active: true,
# sign_in_count: 1,
# };
#
let user2 = User {
email: String::from("another@example.com"),
username: String::from("anotherusername567"),
active: user1.active,
sign_in_count: user1.sign_in_count,
};
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-06/src/main.rs:here}}
```
<span class="caption">示例 5-6创建 `User` 新实例,其使用了一些来自 `user1` 的值</span>
<span class="caption">示例 5-6使用 `user1` 中的一个值创建一个新的 `User` 实例</span>
使用结构体更新语法,我们可以通过更少的代码来达到相同的效果,如示例 5-7 所示。`..` 语法指定了剩余未显式设置值的字段应有与给定实例对应字段相同的值。
```rust
# struct User {
# username: String,
# email: String,
# sign_in_count: u64,
# active: bool,
# }
#
# let user1 = User {
# email: String::from("someone@example.com"),
# username: String::from("someusername123"),
# active: true,
# sign_in_count: 1,
# };
#
let user2 = User {
email: String::from("another@example.com"),
username: String::from("anotherusername567"),
..user1
};
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-07/src/main.rs:here}}
```
<span class="caption">示例 5-7使用结构体更新语法为一个 `User` 实例设置新的 `email` 和 `username` 值,不过其余值来自 `user1` 变量中实例的字段</span>
<span class="caption">示例 5-7使用结构体更新语法为一个 `User` 实例设置一个新的 `email` 值,不过其余值来自 `user1` 变量中实例的字段</span>
示例 5-7 中的代码也在 `user2` 中创建了一个新实例,其有不同的 `email``username` 值不过 `active``sign_in_count` 字段的值与 `user1` 相同。
示例 5-7 中的代码也在 `user2` 中创建了一个新实例,其有不同的 `email` 值,不过 `username``active``sign_in_count` 字段的值与 `user1` 相同。`...user1` 必须放在最后,以指定其余的字段应从 `user1` 的相应字段中获取其值,但我们可以选择以任何顺序为任意字段指定值,而不用考虑结构体定义中字段的顺序。
请注意,结构更新语法就像带有 `=` 的赋值,因为它移动了数据,就像我们在[“变量与数据交互的方式(一):移动”][move]<!-- ignore -->部分讲到的一样。在这个例子中,我们在创建 `user2` 后不能再使用 `user1`,因为 `user1``username` 字段中的 `String` 被移到 `user2` 中。如果我们给 `user2``email``username` 都赋予新的 `String` 值,从而只使用 `user1``active``sign_in_count` 值,那么 `user1` 在创建 `user2` 后仍然有效。`active` 和 `sign_in_count` 的类型是实现 `Copy` trait 的类型,所以我们在[“变量与数据交互的方式(二):克隆”][copy]<!-- ignore -->部分讨论的行为同样适用。
### 使用没有命名字段的元组结构体来创建不同的类型
也可以定义与元组(在第 3 章讨论过)类似的结构体,称为 **元组结构体***tuple structs*)。元组结构体有着结构体名称提供的含义,但没有具体的字段名,只有字段的类型。当你想给整个元组取一个名字,并使元组成为与其他元组不同的类型时,元组结构体是很有用的,这时像常规结构体那样为每个字段命名就显得多余和形式化了。
也可以定义与元组(在第 3 章讨论过)类似的结构体,称为**元组结构体***tuple struct*)。元组结构体有着结构体名称提供的含义,但没有具体的字段名,只有字段的类型。当你想给整个元组取一个名字,并使元组成为与其他元组不同的类型时,元组结构体是很有用的,这时像常规结构体那样为每个字段命名就显得多余和形式化了。
要定义元组结构体,以 `struct` 关键字和结构体名开头并后跟元组中的类型。例如,下面是两个分别叫做 `Color``Point` 元组结构体的定义和用法:
```rust
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/no-listing-01-tuple-structs/src/main.rs}}
```
注意 `black``origin` 值的类型不同,因为它们是不同的元组结构体的实例。你定义的每一个结构体有其自己的类型,即使结构体中的字段有着相同的类型。例如,一个获取 `Color` 类型参数的函数不能接受 `Point` 作为参数,即便这两个类型都由三个 `i32` 值组成。在其他方面,元组结构体实例类似于元组:可以将其解构为单独的部分,也可以使用 `.` 后跟索引来访问单独的值,等等。
### 没有任何字段的类单元结构体
我们也可以定义一个没有任何字段的结构体!它们被称为 **类单元结构体***unit-like structs*)因为它们类似于 `()`,即 unit 类型。类单元结构体常常在你想要在某个类型上实现 trait 但不需要在类型中存储数据的时候发挥作用。我们将在第 10 章介绍 trait。
我们也可以定义一个没有任何字段的结构体!它们被称为**类单元结构体***unit-like structs*),因为它们类似于 `()`,即[“元组类型”][tuples]<!-- ignore -->一节中提到的 unit 类型。类单元结构体常常在你想要在某个类型上实现 trait 但不需要在类型中存储数据的时候发挥作用。我们将在第 10 章介绍 trait。下面是一个声明和实例化一个名为 `AlwaysEqual` 的 unit 结构的例子。
```rust
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/no-listing-04-unit-like-structs/src/main.rs}}
```
要定义 `AlwaysEqual`,我们使用 `struct` 关键字,我们想要的名称,然后是一个分号。不需要花括号或圆括号!然后,我们可以以类似的方式在 `subject` 变量中获得 `AlwaysEqual` 的实例:使用我们定义的名称,不需要任何花括号或圆括号。想象一下,我们将实现这个类型的行为,即每个实例始终等于每一个其他类型的实例,也许是为了获得一个已知的结果以便进行测试。我们不需要任何数据来实现这种行为,你将在第十章中,看到如何定义特性并在任何类型上实现它们,包括类似 unit 的结构体。
> ### 结构体数据的所有权
>
> 在示例 5-1 中的 `User` 结构体的定义中,我们使用了自身拥有所有权的 `String` 类型而不是 `&str` 字符串 slice 类型。这是一个有意而为之的选择,因为我们想要这个结构体拥有它所有的数据,为此只要整个结构体是有效的话其数据也是有效的。
>
> 可以使结构体存储被其他对象拥有的数据的引用,不过这么做的话需要用上 **生命周期***lifetimes*),这是一个第 10 章会讨论的 Rust 功能。生命周期确保结构体引用的数据有效性跟结构体本身保持一致。如果你尝试在结构体中存储一个引用而不指定生命周期将是无效的,比如这样:
> 可以使结构体存储被其他对象拥有的数据的引用,不过这么做的话需要用上**生命周期***lifetime*),这是一个第 10 章会讨论的 Rust 功能。生命周期确保结构体引用的数据有效性跟结构体本身保持一致。如果你尝试在结构体中存储一个引用而不指定生命周期将是无效的,比如这样:
>
> <span class="filename">文件名: src/main.rs</span>
>
> <!-- CAN'T EXTRACT SEE https://github.com/rust-lang/mdBook/issues/1127 -->
>
> ```rust,ignore,does_not_compile
> struct User {
> active: bool,
> username: &str,
> email: &str,
> sign_in_count: u64,
> active: bool,
> }
>
> fn main() {
@ -216,18 +126,38 @@ let origin = Point(0, 0, 0);
>
> 编译器会抱怨它需要生命周期标识符:
>
> ```text
> ```console
> $ cargo run
> Compiling structs v0.1.0 (file:///projects/structs)
> error[E0106]: missing lifetime specifier
> -->
> --> src/main.rs:3:15
> |
> 3 | username: &str,
> | ^ expected named lifetime parameter
> |
> help: consider introducing a named lifetime parameter
> |
> 1 ~ struct User<'a> {
> 2 | active: bool,
> 3 ~ username: &'a str,
> |
> 2 | username: &str,
> | ^ expected lifetime parameter
>
> error[E0106]: missing lifetime specifier
> -->
> --> src/main.rs:4:12
> |
> 3 | email: &str,
> | ^ expected lifetime parameter
> 4 | email: &str,
> | ^ expected named lifetime parameter
> |
> help: consider introducing a named lifetime parameter
> |
> 1 ~ struct User<'a> {
> 2 | active: bool,
> 3 | username: &str,
> 4 ~ email: &'a str,
> |
>
> For more information about this error, try `rustc --explain E0106`.
> error: could not compile `structs` due to 2 previous errors
> ```
>
> 第 10 章会讲到如何修复这个问题以便在结构体中存储引用,不过现在,我们会使用像 `String` 这类拥有所有权的类型来替代 `&str` 这样的引用以修正这个错误。
@ -238,3 +168,7 @@ after running update-rustc.sh:
pbcopy < listings/ch05-using-structs-to-structure-related-data/no-listing-02-reference-in-struct/output.txt
paste above
add `> ` before every line -->
[tuples]: ch03-02-data-types.html#元组类型
[move]: ch04-01-what-is-ownership.html#变量与数据交互的方式一移动
[copy]: ch04-01-what-is-ownership.html#变量与数据交互的方式二克隆

View File

@ -7,27 +7,15 @@
<span class="filename">文件名: src/main.rs</span>
```rust
fn main() {
let width1 = 30;
let height1 = 50;
println!(
"The area of the rectangle is {} square pixels.",
area(width1, height1)
);
}
fn area(width: u32, height: u32) -> u32 {
width * height
}
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-08/src/main.rs:all}}
```
<span class="caption">示例 5-8通过分别指定长方形的宽和高的变量来计算长方形面积</span>
现在使用 `cargo run` 运行程序:
```text
The area of the rectangle is 1500 square pixels.
```console
{{#include ../listings/ch05-using-structs-to-structure-related-data/listing-05-08/output.txt}}
```
虽然示例 5-8 可以运行,并在调用 `area` 函数时传入每个维度来计算出长方形的面积,不过我们可以做的更好。宽度和高度是相关联的,因为他们在一起才能定义一个长方形。
@ -35,10 +23,10 @@ The area of the rectangle is 1500 square pixels.
这些代码的问题突显在 `area` 的签名上:
```rust,ignore
fn area(width: u32, height: u32) -> u32 {
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-08/src/main.rs:here}}
```
函数 `area` 本应该计算一个长方形的面积,不过函数却有两个参数。这两个参数是相关联的,不过程序本身却没有表现出这一点。将长度和宽度组合在一起将更易懂也更易处理。第 3 章的 [“元组类型”][the-tuple-type] 部分已经讨论过了一种可行的方法:元组。
函数 `area` 本应该计算一个长方形的面积,不过函数却有两个参数。这两个参数是相关联的,不过程序本身却没有表现出这一点。将长度和宽度组合在一起将更易懂也更易处理。第 3 章的[“元组类型”][the-tuple-type]<!-- ignore -->章节已经讨论过了一种可行的方法:元组。
### 使用元组重构
@ -47,18 +35,7 @@ fn area(width: u32, height: u32) -> u32 {
<span class="filename">文件名: src/main.rs</span>
```rust
fn main() {
let rect1 = (30, 50);
println!(
"The area of the rectangle is {} square pixels.",
area(rect1)
);
}
fn area(dimensions: (u32, u32)) -> u32 {
dimensions.0 * dimensions.1
}
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-09/src/main.rs}}
```
<span class="caption">示例 5-9使用元组来指定长方形的宽高</span>
@ -74,23 +51,7 @@ fn area(dimensions: (u32, u32)) -> u32 {
<span class="filename">文件名: src/main.rs</span>
```rust
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let rect1 = Rectangle { width: 30, height: 50 };
println!(
"The area of the rectangle is {} square pixels.",
area(&rect1)
);
}
fn area(rectangle: &Rectangle) -> u32 {
rectangle.width * rectangle.height
}
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-10/src/main.rs}}
```
<span class="caption">示例 5-10定义 `Rectangle` 结构体</span>
@ -103,38 +64,28 @@ fn area(rectangle: &Rectangle) -> u32 {
### 通过派生 trait 增加实用功能
如果能够在调试程序时打印出 `Rectangle` 实例来查看其所有字段的值就更好了。示例 5-11 像前面章节那样尝试使用 `println!`。但这并不行。
如果能够在调试程序时打印出 `Rectangle` 实例来查看其所有字段的值就更好了。示例 5-11 像前面章节那样尝试使用 [`println!` 宏][println]。但这并不行。
<span class="filename">文件名: src/main.rs</span>
```rust,ignore,does_not_compile
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let rect1 = Rectangle { width: 30, height: 50 };
println!("rect1 is {}", rect1);
}
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-11/src/main.rs}}
```
<span class="caption">示例 5-11尝试打印出 `Rectangle` 实例</span>
当我们运行这个代码时,会出现带有如下核心信息的错误:
```text
error[E0277]: `Rectangle` doesn't implement `std::fmt::Display`
```console
{{#include ../listings/ch05-using-structs-to-structure-related-data/listing-05-11/output.txt:3}}
```
`println!` 宏能处理很多类型的格式,不过,`{}` 默认告诉 `println!` 使用被称为 `Display` 的格式:意在提供给直接终端用户查看的输出。目前为止见过的基本类型都默认实现了 `Display`,因为它就是向用户展示 `1` 或其他任何基本类型的唯一方式。不过对于结构体,`println!` 应该用来输出的格式是不明确的因为这有更多显示的可能性是否需要逗号需要打印出大括号吗所有字段都应该显示吗由于这种不确定性Rust 不会尝试猜测我们的意图,所以结构体并没有提供一个 `Display` 实现。
但是如果我们继续阅读错误,将会发现这个有帮助的信息:
```text
= help: the trait `std::fmt::Display` is not implemented for `Rectangle`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
```console
{{#include ../listings/ch05-using-structs-to-structure-related-data/listing-05-11/output.txt:9:10}}
```
让我们来试试!现在 `println!` 宏调用看起来像 `println!("rect1 is {:?}", rect1);` 这样。在 `{}` 中加入 `:?` 指示符告诉 `println!` 我们想要使用叫做 `Debug` 的输出格式。`Debug` 是一个 trait它允许我们以一种对开发者有帮助的方式打印结构体以便当我们调试代码时能看到它的值。
@ -142,53 +93,59 @@ error[E0277]: `Rectangle` doesn't implement `std::fmt::Display`
这样调整后再次运行程序。见鬼了!仍然能看到一个错误:
```text
error[E0277]: `Rectangle` doesn't implement `std::fmt::Debug`
{{#include ../listings/ch05-using-structs-to-structure-related-data/output-only-01-debug/output.txt:3}}
```
不过编译器又一次给出了一个有帮助的信息:
```text
= help: the trait `std::fmt::Debug` is not implemented for `Rectangle`
= note: add `#[derive(Debug)]` or manually implement `std::fmt::Debug`
{{#include ../listings/ch05-using-structs-to-structure-related-data/output-only-01-debug/output.txt:9:10}}
```
Rust **确实** 包含了打印出调试信息的功能,不过我们必须为结构体显式选择这个功能。为此,在结构体定义之前加上 `#[derive(Debug)]` 注解,如示例 5-12 所示:
Rust **确实**包含了打印出调试信息的功能,不过我们必须为结构体显式选择这个功能。为此,在结构体定义之前加上外部属性 `#[derive(Debug)]`,如示例 5-12 所示:
<span class="filename">文件名: src/main.rs</span>
```rust
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let rect1 = Rectangle { width: 30, height: 50 };
println!("rect1 is {:?}", rect1);
}
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-12/src/main.rs}}
```
<span class="caption">示例 5-12增加注解来派生 `Debug` trait并使用调试格式打印 `Rectangle` 实例</span>
<span class="caption">示例 5-12增加属性来派生 `Debug` trait并使用调试格式打印 `Rectangle` 实例</span>
现在我们再运行这个程序时,就不会有任何错误,并会出现如下输出:
```text
rect1 is Rectangle { width: 30, height: 50 }
```console
{{#include ../listings/ch05-using-structs-to-structure-related-data/listing-05-12/output.txt}}
```
好极了!这并不是最漂亮的输出,不过它显示这个实例的所有字段,毫无疑问这对调试有帮助。当我们有一个更大的结构体时,能有更易读一点的输出就好了,为此可以使用 `{:#?}` 替换 `println!` 字符串中的 `{:?}`。如果在这个例子中使用了 `{:#?}` 风格的话,输出会看起来像这样:
```text
rect1 is Rectangle {
width: 30,
height: 50
}
```console
{{#include ../listings/ch05-using-structs-to-structure-related-data/output-only-02-pretty-debug/output.txt}}
```
Rust 为我们提供了很多可以通过 `derive` 注解来使用的 trait他们可以为我们的自定义类型增加实用的行为。附录 C 中列出了这些 trait 和行为。第 10 章会介绍如何通过自定义行为来实现这些 trait同时还有如何创建你自己的 trait
另一种使用 `Debug` 格式打印数值的方法是使用 [`dbg!` 宏][dbg]<!-- ignore -->。`dbg!` 宏接收一个表达式的所有权,打印出你代码中 `dbg!` 宏调用的文件和行号,以及该表达式的结果值,并返回该值的所有权。调用 `dbg!` 宏会打印到标准错误控制台流(`stderr`),而不是 `println!`,后者会打印到标准输出控制台流(`stdout`)。我们将在[第 12 章 “将错误信息写入标准错误而不是标准输出” 一节][err]<!-- ignore -->中更多地讨论 `stderr``stdout`。下面是一个例子,我们对分配给 `width` 字段的值以及 `rect1` 中整个结构的值感兴趣。
我们的 `area` 函数是非常特殊的,它只计算长方形的面积。如果这个行为与 `Rectangle` 结构体再结合得更紧密一些就更好了,因为它不能用于其他类型。现在让我们看看如何继续重构这些代码,来将 `area` 函数协调进 `Rectangle` 类型定义的 `area` **方法** 中。
```rust
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/no-listing-05-dbg-macro/src/main.rs}}
```
[the-tuple-type]: ch03-02-data-types.html#the-tuple-type
我们可以把 `dbg!` 放在表达式 `30 * scale` 周围,因为 `dbg!` 返回表达式的值的所有权,所以 `width` 字段将获得相同的值,就像我们在那里没有 `dbg!` 调用一样。我们不希望 `dbg!` 拥有 `rect1` 的所有权,所以我们在下一次调用中使用对 `dbg!` 的引用。下面是这个例子的输出结果:
```console
{{#include ../listings/ch05-using-structs-to-structure-related-data/no-listing-05-dbg-macro/output.txt}}
```
我们可以看到第一点输出来自 *src/main.rs* 第 10 行,我们正在调试表达式 `30 * scale`其结果值是60为整数实现的 `Debug` 格式化是只打印它们的值)。在 *src/main.rs* 第 14行 的 `dbg!` 调用输出 `&rect1` 的值,即 `Recangle` 结构。这个输出使用了更为易读的 `Debug` 格式。当你试图弄清楚你的代码在做什么时,`dbg!` 宏可能真的很有帮助!
除了 `Debug` traitRust 还为我们提供了很多可以通过 `derive` 属性来使用的 trait他们可以为我们的自定义类型增加实用的行为。[附录 C][app-c]<!--
ignore --> 中列出了这些 trait 和行为。第十章会介绍如何通过自定义行为来实现这些 trait同时还有如何创建你自己的 trait。除了 `derive` 之外,还有很多属性;更多信息请参见 [《Rust 参考手册》][attributes] 的 Attributes 部分。
我们的 `area` 函数非常特殊,它只计算长方形的面积。如果这个行为与 `Rectangle` 结构体再结合得更紧密一些就更好了,因为它不能用于其他类型。现在让我们看看如何继续重构这些代码,来将 `area` 函数协调进 `Rectangle` 类型定义的 `area` **方法**中。
[the-tuple-type]: ch03-02-data-types.html#元组类型
[app-c]: appendix-03-derivable-traits.md
[println]: https://rustwiki.org/zh-CN/std/macro.println.html
[dbg]: https://rustwiki.org/zh-CN/std/macro.dbg.html
[err]: ch12-06-writing-to-stderr-instead-of-stdout.html
[attributes]: https://rustwiki.org/zh-CN/reference/attributes.html

View File

@ -9,38 +9,31 @@
<span class="filename">文件名: src/main.rs</span>
```rust
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
fn main() {
let rect1 = Rectangle { width: 30, height: 50 };
println!(
"The area of the rectangle is {} square pixels.",
rect1.area()
);
}
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-13/src/main.rs}}
```
<span class="caption">示例 5-13`Rectangle` 结构体上定义 `area` 方法</span>
为了使函数定义于 `Rectangle` 的上下文中,我们开始了一个 `impl` 块(`impl` 是 *implementation* 的缩写)。接着将 `area` 函数移动到 `impl` 大括号中,并将签名中的第一个(在这里也是唯一一个)参数和函数体中其他地方的对应参数改成 `self`。然后在 `main` 中将我们先前调用 `area` 方法并传递 `rect1` 作为参数的地方,改成使用 **方法语法***method syntax*)在 `Rectangle` 实例上调用 `area` 方法。方法语法获取一个实例并加上一个点号,后跟方法名、圆括号以及任何参数。
为了使函数定义于 `Rectangle` 的上下文中,我们开始了一个 `impl` 块(`impl` 是 *implementation* 的缩写),这个 `impl` 块中的所有内容都将与 `Rectangle` 类型相关联。接着将 `area` 函数移动到 `impl` 大括号中,并将签名中的第一个(在这里也是唯一一个)参数和函数体中其他地方的对应参数改成 `self`。然后在 `main` 中将我们先前调用 `area` 方法并传递 `rect1` 作为参数的地方,改成使用**方法语法***method syntax*)在 `Rectangle` 实例上调用 `area` 方法。方法语法获取一个实例并加上一个点号,后跟方法名、圆括号以及任何参数。
`area` 的签名中,使用 `&self` 来替代 `rectangle: &Rectangle`因为该方法位于 `impl Rectangle` 上下文中所以 Rust 知道 `self` 的类型是 `Rectangle`。注意仍然需要在 `self` 前面加上 `&`,就像 `&Rectangle` 一样。方法可以选择获取 `self` 的所有权,或者像我们这里一样不可变地借用 `self`,或者可变地借用 `self`,就跟其他参数一样。
`area` 的签名中,使用 `&self` 来替代 `rectangle: &Rectangle``&self` 实际上是 `self: &Self` 的缩写。在一个 `impl` 块中,`Self` 类型是 `impl` 块的类型的别名。方法的第一个参数必须有一个名为 `self` 的`Self` 类型的参数,所以 Rust 让你在第一个参数位置上只用 `self` 这个名字来缩写。注意,我们仍然需要在 `self` 前面使用 `&` 来表示这个方法借用了 `Self` 实例,就像我们在 `rectangle: &Rectangle` 中做的那样。方法可以选择获得 `self` 的所有权,或者像我们这里一样不可变地借用 `self`,或者可变地借用 `self`,就跟其他参数一样。
这里选择 `&self` 的理由跟在函数版本中使用 `&Rectangle` 是相同的:我们并不想获取所有权,只希望能够读取结构体中的数据,而不是写入。如果想要在方法中改变调用方法的实例,需要将第一个参数改为 `&mut self`。通过仅仅使用 `self` 作为第一个参数来使方法获取实例的所有权是很少见的;这种技术通常用在当方法将 `self` 转换成别的实例的时候,这时我们想要防止调用者在转换之后使用原始的实例。
使用方法替代函数,除了可使用方法语法和不需要在每个函数签名中重复 `self` 的类型之外,其主要好处在于组织性。我们将某个类型实例能做的所有事情都一起放入 `impl` 块中,而不是让将来的用户在我们的库中到处寻找 `Rectangle` 的功能。
请注意,我们可以选择将方法的名称与结构中的一个字段相同。例如,我们可以在 `Rectangle` 上定义一个方法,并命名为 `width`
<span class="filename">文件名: src/main.rs</span>
```rust
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/no-listing-06-method-field-interaction/src/main.rs:here}}
```
在这里,我们选择让 `width` 方法的行为是如果实例的 `width` 字段的值大于 0返回 `true`。如果该值为 0则返回 `false`:我们可以在同名的方法中使用一个字段。我们可以在同名的方法中使用一个字段来达到任何目的。在 `main` 中,当我们在 `rect1.width` 后面加上括号时。Rust 知道我们指的是方法 `width`。当我们不使用圆括号时Rust 知道我们指的是字段 `width`
通常,但并不总是如此,与字段同名的方法将被定义为只返回字段中的值,而不做其他事情。这样的方法被称为 *getters*Rust 并不像其他一些语言那样为结构字段自动实现它们。Getters 很有用,因为你可以把字段变成私有的,但方法是公共的,这样就可以把对字段的只读访问作为该类型公共 API 的一部分。我们将在第七章中讨论什么是公有和私有,以及如何将一个字段或方法指定为公有或私有。
> ### `->` 运算符到哪去了?
>
> 在 C/C++ 语言中,有两个不同的运算符来调用方法:`.` 直接在对象上调用方法,而 `->` 在一个对象的指针上调用方法这时需要先解引用dereference指针。换句话说如果 `object` 是一个指针,那么 `object->something()` 就像 `(*object).something()` 一样。
@ -49,6 +42,7 @@ fn main() {
>
> 他是这样工作的:当使用 `object.something()` 调用方法时Rust 会自动为 `object` 添加 `&`、`&mut` 或 `*` 以便使 `object` 与方法签名匹配。也就是说,这些代码是等价的:
>
> <!-- CAN'T EXTRACT SEE BUG https://github.com/rust-lang/mdBook/issues/1127 -->
> ```rust
> # #[derive(Debug,Copy,Clone)]
> # struct Point {
@ -79,14 +73,7 @@ fn main() {
<span class="filename">文件名: src/main.rs</span>
```rust,ignore
fn main() {
let rect1 = Rectangle { width: 30, height: 50 };
let rect2 = Rectangle { width: 10, height: 40 };
let rect3 = Rectangle { width: 60, height: 45 };
println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));
}
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-14/src/main.rs}}
```
<span class="caption">示例 5-14使用还未实现的 `can_hold` 方法</span>
@ -103,21 +90,7 @@ Can rect1 hold rect3? false
<span class="filename">文件名: src/main.rs</span>
```rust
# #[derive(Debug)]
# struct Rectangle {
# width: u32,
# height: u32,
# }
#
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-15/src/main.rs:here}}
```
<span class="caption">示例 5-15`Rectangle` 上实现 `can_hold` 方法,它获取另一个 `Rectangle` 实例作为参数</span>
@ -126,24 +99,14 @@ impl Rectangle {
### 关联函数
`impl` 块的另一个有用的功能是:允许在 `impl` 块中定义 **不**`self` 作为参数的函数。这被称为 **关联函数***associated functions*),因为它们与结构体相关联。它们仍是函数而不是方法,因为它们并不作用于一个结构体的实例。你已经使用过 `String::from` 关联函数了
所有在 `impl` 块中定义的函数被称为**关联函数***associated function*),因为它们与 `impl` 后面命名的类型相关。我们可以定义不以 `self` 为第一参数的关联函数(因此不是方法),因为它们并不作用于一个结构体的实例。我们已经使用了一个这样的函数,`String::from` 函数,它是在 `String` 类型上定义的
关联函数经常被用作返回一个结构体新实例的构造函数。例如我们可以提供一个关联函数,它接受一个维度参数并且同时作为宽和高,这样可以更轻松的创建一个正方形 `Rectangle` 而不必指定两次同样的值:
<span class="filename">文件名: src/main.rs</span>
```rust
# #[derive(Debug)]
# struct Rectangle {
# width: u32,
# height: u32,
# }
#
impl Rectangle {
fn square(size: u32) -> Rectangle {
Rectangle { width: size, height: size }
}
}
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/no-listing-03-associated-functions/src/main.rs:here}}
```
使用结构体名和 `::` 语法来调用这个关联函数:比如 `let sq = Rectangle::square(3);`。这个方法位于结构体的命名空间中:`::` 语法用于关联函数和模块创建的命名空间。第 7 章会讲到模块。
@ -153,23 +116,7 @@ impl Rectangle {
每个结构体都允许拥有多个 `impl` 块。例如,示例 5-16 中的代码等同于示例 5-15但每个方法有其自己的 `impl` 块。
```rust
# #[derive(Debug)]
# struct Rectangle {
# width: u32,
# height: u32,
# }
#
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
impl Rectangle {
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
{{#rustdoc_include ../listings/ch05-using-structs-to-structure-related-data/listing-05-16/src/main.rs:here}}
```
<span class="caption">示例 5-16使用多个 `impl` 块重写示例 5-15</span>
@ -178,6 +125,6 @@ impl Rectangle {
## 总结
结构体让你可以创建出在你的领域中有意义的自定义类型。通过结构体,我们可以将相关联的数据片段联系起来并命名它们,这样可以使得代码更加清晰。方法允许为结构体实例指定行为,而关联函数将特定功能置于结构体的命名空间中并且无需一个实例
结构体让你可以创建出在你的领域中有意义的自定义类型。通过结构体,我们可以将相关联的数据片段联系起来并命名它们,这样可以使得代码更加清晰。`impl` 块中,你可以定义与你的类型相关联的函数,而方法是一种相关联的函数,让你指定结构体的实例所具有的行为
但结构体并不是创建自定义类型的唯一方法:让我们转向 Rust 的枚举功能,为你的工具箱再添一个工具。