mirror of
https://github.com/gnu4cn/rust-lang-zh_CN.git
synced 2026-08-19 04:33:27 +08:00
Updated src/structs/method_syntax.md'.
This commit is contained in:
@@ -1,3 +1,47 @@
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
#[derive(Debug)]
|
||||
struct Rectangle {
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
impl Rectangle {
|
||||
fn area(&self) -> u32 {
|
||||
self.width * self.height
|
||||
}
|
||||
|
||||
fn width(&self) -> bool {
|
||||
self.width > 0
|
||||
}
|
||||
|
||||
fn can_hold(&self, other: &Rectangle) -> bool {
|
||||
(self.width > other.width && self.height > other.height) ||
|
||||
(self.width > other.height && self.height > other.width)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let rect1 = Rectangle {
|
||||
width: 30,
|
||||
height: 50,
|
||||
};
|
||||
|
||||
let rect2 = Rectangle {
|
||||
width: 10,
|
||||
height: 40,
|
||||
};
|
||||
|
||||
let rect3 = Rectangle {
|
||||
width: 60,
|
||||
height: 45,
|
||||
};
|
||||
|
||||
let rect4 = Rectangle {
|
||||
width: 40,
|
||||
height: 25,
|
||||
};
|
||||
|
||||
|
||||
println! ("rect1 可以容纳 rect2 吗?{}", rect1.can_hold(&rect2));
|
||||
println! ("rect1 可以容纳 rect3 吗?{}", rect1.can_hold(&rect3));
|
||||
println! ("rect1 可以容纳 rect4 吗?{}", rect1.can_hold(&rect4));
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
为了定义结构体,我们要输入关键字 `struct` 并命名整个结构体。结构体的名称应描述这些分组在一起数据的意义。然后,在花括号内,咱们定义那些数据片段的名字与类型,我们称之为 *字段,field*。例如,下面清单 5-1 展示了个存储用户账户信息的结构体。
|
||||
|
||||
|
||||
<a name="listing_5-1"></a>
|
||||
```rust
|
||||
struct User {
|
||||
active: bool,
|
||||
@@ -15,13 +14,13 @@ struct User {
|
||||
}
|
||||
```
|
||||
|
||||
<a name="listing_5-1"></a>
|
||||
**清单 5-1**:`User` 结构体定义
|
||||
|
||||
|
||||
在咱们定义了结构体后要使用他,我们就要通过为每个字段指定具体值,创建该结构体的 *实例,instance*。我们通过指明结构体的名字创建实例,然后添加包含着一些 `key: value` 对的花括号,其中值为字段的名字,值为我们打算存储在这些字段中的数据。我们不必以我们在结构体中声明字段的同一顺序指定这些字段。换句话说,结构体的定义就像类型的通用模板,而实例会以特定数据填充该模板创建该类型的值。例如,我们可以声明某名特定用户,如下清单 5-2 中所示。
|
||||
|
||||
|
||||
<a name="listing_5-2"></a>
|
||||
文件名:`src/main.rs`
|
||||
|
||||
```rust
|
||||
@@ -35,13 +34,13 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
<a name="listing_5-2"></a>
|
||||
**清单 5-2**:创建 `User` 结构体的实例
|
||||
|
||||
|
||||
要获取结构体中的某个特定值,我们使用点表示法。例如,要访问这名用户的电子邮件地址,我们使用 `user1.email`。当实例是可变的时,我们可通过使用点表示法并赋值到某个特定字段更改值。下面清单 5-3 展示了如何更改某个可变 `User` 实例的 `email` 字段中的值。
|
||||
|
||||
|
||||
<a name="listing_5-3"></a>
|
||||
文件名:`src/main.rs`
|
||||
|
||||
```rust
|
||||
@@ -57,6 +56,7 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
<a name="listing_5-3"></a>
|
||||
**清单 5-3**:修改某个 `User` 实例的 `email` 字段中的值
|
||||
|
||||
要注意整个实例必须是可变的;Rust 不允许我们只将一些字段标记为可变。与任何表达式一样,我们可将结构体的新实例构造为函数体中的最后一个表达式,从而隐式地返回该新实例。
|
||||
@@ -85,7 +85,6 @@ fn build_user(email: String, username: String) -> User {
|
||||
由于清单 5-4 中的参数名字和结构体字段名字完全相同,我们可以使用 *字段初始化简写,field init shorthand* 语法重写 `build_user`,使其行为完全相同却不会重复 `username` 和 `email`,如下清单 5-5 中所示。
|
||||
|
||||
|
||||
<a name="listing_5-5"></a>
|
||||
```rust
|
||||
fn build_user(email: String, username: String) -> User {
|
||||
User {
|
||||
@@ -97,6 +96,7 @@ fn build_user(email: String, username: String) -> User {
|
||||
}
|
||||
```
|
||||
|
||||
<a name="listing_5-5"></a>
|
||||
**清单 5-5**:由于 `username` 与 `email` 两个参数有着与结构体字段同样的名字,而使用了字段初始化简写的 `build_user` 函数
|
||||
|
||||
|
||||
@@ -110,7 +110,6 @@ fn build_user(email: String, username: String) -> User {
|
||||
首先,在下面清单 5-6 中,我们展示如何以常规方式,在没有更新语法下,于 `user2` 中创建一个新的 `User` 实例。我们设置了 `email` 的新值,但但其余值则使用咱们在 [清单 5-2](#listing_5-2) 中创建的 `user1` 中的相同值。
|
||||
|
||||
|
||||
<a name="listing_5-6"></a>
|
||||
```rust
|
||||
fn main() {
|
||||
// --跳过代码--
|
||||
@@ -124,13 +123,13 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
<a name="listing_5-6"></a>
|
||||
**清单 5-6**:创建一个用到 `user1` 中除一个值外的所有值的新 `User` 实例
|
||||
|
||||
|
||||
使用结构体更新语法,我们可以用更少代码实现相同效果,如下清单 5-7 中所示。`..` 这种语法表示未显式设置的其余字段,应有着与给定实例中的字段相同的值。
|
||||
|
||||
|
||||
<a name="listing_5-7"></a>
|
||||
```rust
|
||||
fn main() {
|
||||
// --跳过代码--
|
||||
@@ -142,6 +141,7 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
<a name="listing_5-7"></a>
|
||||
*清单 5-7:使用结构体更新语法设置 `User` 实例的新 `email` 值,但使用 `user1` 中的其余值*
|
||||
|
||||
清单 5-7 中的代码同样在 `user2` 中创建了个实例,其有着一个 `email` 的不同值,但有着 `user1` 中 `username`、`active` 和 `sign_in_count` 等字段的相同值。`..user1` 必须放在最后,以指定全部其余字段都应从 `user1` 中的相应字段获取他们的值,但我们可以选择以任意顺序指定咱们想要数量字段的值,而无关乎结构体定义中的字段顺序。
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
我们来以 Cargo 构造一个名为 `rectangles` 的新二进制项目,其将取以像素为单位指定的矩形的宽和高,并计算矩形面积。下面清单 5-8 展示了个简短程序,有着在咱们项目的 `src/main.rs` 中完成这点的一种方式。
|
||||
|
||||
|
||||
<a name="listing_5-8"></a>
|
||||
文件名:`src/main.rs`
|
||||
|
||||
```rust
|
||||
@@ -24,6 +23,7 @@ fn area(width: u32, height: u32) -> u32 {
|
||||
}
|
||||
```
|
||||
|
||||
<a name="listing_5-8"></a>
|
||||
**清单 5-8**:计算由单独的宽度和高度变量指定的矩形面积
|
||||
|
||||
|
||||
@@ -56,7 +56,6 @@ fn area(width: u32, height: u32) -> u32 {
|
||||
下面清单 5-9 展示了咱们程序使用元组的另一版本。
|
||||
|
||||
|
||||
<a name="listing_5-9"></a>
|
||||
文件名:`src/main.rs`
|
||||
|
||||
```rust
|
||||
@@ -74,6 +73,7 @@ fn area(dimensions: (u32, u32)) -> u32 {
|
||||
}
|
||||
```
|
||||
|
||||
<a name="listing_5-9"></a>
|
||||
**清单 5-9**:以元组指定矩形的宽和高
|
||||
|
||||
从某种意义上说,这个程序更好。元组让我们增加了一点结构,进而我们现在只传递一个参数。但从另一方面来说,这个版本更不清楚:元组没有命名其元素,因此我们不得不索引进到元组的各个部分,使我们的计算不那么直观。
|
||||
@@ -86,7 +86,6 @@ fn area(dimensions: (u32, u32)) -> u32 {
|
||||
我们使用结构体通过标记数据来添加含义。我们可将我们正使用的元组,转换为一个有着整体名字以及各部分名字的结构体,如下清单 5-10 中所示。
|
||||
|
||||
|
||||
<a name="listing_5-10"></a>
|
||||
文件名:`src/main.rs`
|
||||
|
||||
```rust
|
||||
@@ -112,6 +111,7 @@ fn area(rectangle: &Rectangle) -> u32 {
|
||||
}
|
||||
```
|
||||
|
||||
<a name="listing_5-10"></a>
|
||||
**清单 5-10**:定义一个 `Rectangle` 结构体
|
||||
|
||||
|
||||
@@ -127,7 +127,6 @@ fn area(rectangle: &Rectangle) -> u32 {
|
||||
我们在调试咱们程序期间,能够打印 `Rectangle` 的实例进而看到其所有字段的值,那将很有用。下面清单 5-11 尝试使用 `println!` 这个宏,正如我们在前几章中曾使用的那样。但这行不通。
|
||||
|
||||
|
||||
<a name="listing_5-11"></a>
|
||||
文件名:`src/main.rs`
|
||||
|
||||
```rust
|
||||
@@ -146,6 +145,7 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
<a name="listing_5-11"></a>
|
||||
*清单 5-11:尝试打印 `Rectangle` 实例*
|
||||
|
||||
|
||||
@@ -190,7 +190,6 @@ error[E0277]: `Rectangle` doesn't implement `Debug`
|
||||
Rust *确实* 包含了打印出调试信息的功能,但我们必须显式地选择使该功能针对我们的结构体可用。要实现这一目的,我们就要在结构体定义之前添加外层属性 `#[derive(Debug)]`,如下清单 5-12 中所示。
|
||||
|
||||
|
||||
<a name="listing_5-12"></a>
|
||||
文件名:`src/main.rs`
|
||||
|
||||
```rust
|
||||
@@ -210,6 +209,7 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
<a name="listing_5-12"></a>
|
||||
**清单 5-12**:添加派生出 `Debug` 特质的属性,并使用调试格式打印 `Rectangle` 的实例
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
我们来修改以 `Rectangle` 实例为参数的 `area` 函数,转而构造一个定义在 `Rectangle` 结构上的 `area` 方法,如下清单 5-13 中所示。
|
||||
|
||||
<a name="listing_5-13"></a>
|
||||
文件名:`src/main.rs`
|
||||
|
||||
```rust
|
||||
@@ -35,6 +34,7 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
<a name="listing_5-13"></a>
|
||||
**清单 5-13**:定义 `Rectangle` 结构体上的 `area` 方法
|
||||
|
||||
为了在 `Rectangle` 的上下文中定义函数,我们开始了个 `Rectangle` 的 `impl`(实现,implementation)代码块。这个 `impl` 代码块中的所有内容都将与 `Rectangle` 类型关联。然后,我们将 `area` 函数移入 `impl` 的花括号中,并把函数签名中的第一个(本示例中的唯一)参数修改为 `self`, 并修改函数体中的各处。在 `main` 中,在我们曾调用 `area` 函数并将 `rect1` 作为参数传递的地方,我们便可使用 *方法语法,method syntax*,调用咱们 `Rectangle` 实例上的 `area` 方法。方法语法位于实例之后:我们添加一个点,后跟方法名称、圆括号及任何的参数。
|
||||
@@ -51,9 +51,9 @@ fn main() {
|
||||
|
||||
出于我们在函数版本中使用 `&Rectangle` 的同一原因,我们在这里选择 `$self`:我们不打算取得所有权,我们只想读取结构体中的数据,而不是写入他。若作为方法执行内容的一部分,咱们打算修改我们于其上调用方法的实例时,我们就要使用 `&mut self` 作为第一个参数。通过只使用 `self` 作为第一个参数让方法取得实例的所有权很少见;这种做法通常会在方法会将 `self` 转换成其他内容,而咱们打算阻止调用者在转换后使用原始实例时用到。
|
||||
|
||||
除了提供方法语法和不必在每个方法的签名中,重复 `self` 的类型外,使用方法而不是函数的主要原因,是为了组织。我们把所有能用类型实例做的事情,都放在一个 `impl` 块中,而不是让我们代码的未来用户,在我们提供的库中不同地方,检索 `Rectangle` 的功能。
|
||||
除了提供方法语法与不必在每个方法的签名中重复 `self` 的类型外,使用方法而不是函数的主要原因是为了组织。我们把所有能对类型实例做的事情都放在一个 `impl` 块中,而不是让我们代码的未来用户,在我们提供的库中不同地方检索 `Rectangle` 的能力。
|
||||
|
||||
请注意,我们可以选择将某个方法,命名为与结构体的某个字段同名。例如,我们可以为 `Rectangle` 定义一个名为 `width` 的方法:
|
||||
请注意,我们可以选择将方法取名为与结构体字段之一同样的名字。例如,我们可以在 `Rectangle` 上定义一个也叫做 `width` 的方法:
|
||||
|
||||
|
||||
文件名:`src/main.rs`
|
||||
@@ -72,39 +72,36 @@ fn main() {
|
||||
};
|
||||
|
||||
if rect1.width() {
|
||||
println! ("该矩形的宽度不为零;其为 {}", rect1.width);
|
||||
println! ("矩形的宽度不为零;其为 {}", rect1.width);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
在这里,我们选择将这个 `width` 方法,构造为在实例的 `width` 字段中值大于 `0` 时,返回 `true`;如果该值为 `0`,则返回 `false`:我们可以在与某个字段的同名方法中,使用这个字段来达到任何目的。在 `main` 中,当我们在 `rect1.width` 后加上括号时,Rust 就知道我们指的是 `width` 这个方法。当我们不使用括号时,Rust 知道我们指的是 `width` 字段。
|
||||
在这里,我们选择将这个 `width` 方法构造为当实例的 `width` 字段中的值大于 `0` 时返回 `true`,并当该值为 `0` 时返回 `false`:我们可出于任何目的在与字段同名的方法中使用该字段。在 `main` 中,当我们在 `rect1.width` 后加上括号时,Rust 就知道我们指的是 `width` 这个方法。在我们未使用括号时,Rust 知道我们指的是 `width` 这个字段。
|
||||
|
||||
通常(但不总是),当我把某个方法,命名为与一个字段同名时,我们就希望他只返回字段中的值,而不做其他任何事情。这样的方法称为 *获取器,getter*,而 Rust 并未像其他语言那样,自动为结构的字段实现获取器。获取器之所以有用,是因为我们可以将该字段,构造为私有,而将该方法构造为公开,从而将对该字段的只读访问,实现为该类型公开 API 的一部分。我们将在 [第 7 章](../packages_crates_and_modules/paths.md#使用-pub-关键字对路径进行暴露) 讨论什么是公开,public 和私有,private,以及如何将字段或方法,指定为公开或私有。
|
||||
通常,但并非总是,当我们给方法取了与字段同样的名字时,我们就会希望他只返回该字段中的值,而不做其他任何事情。像这样的方法称为 *获取器,getter*,而 Rust 并未像一些别的语言那样自动对结构字段实现他们。获取器很有用,因为我们可以将字段构造为私有,而将方法构造为公开,从而作为类型公开 API 的一部分,实现对字段的只读访问。我们将在 [第 7 章](../packages_crates_and_modules/paths.md#使用-pub-关键字对路径进行暴露) 中讨论什么是公开和私有,以及如何将字段或方法指定为公开或私有。
|
||||
|
||||
|
||||
> **`->` 操作符在哪里?**
|
||||
>
|
||||
> 在 C 和 C++ 中,有两种不同的操作符用于调用方法:如果是直接调用对象上的方法,咱们会使用 `.`;如果是调用到对象的某个指针上的方法,并且需要首先解引用该指针时,则要使用 `->`。换句话说,如果 `object` 是个指针,则 `object->something()` 类似于 `(*object).something()`。
|
||||
> 在 C 和 C++ 中,有两种不同的运算符用于调用方法:当咱们直接在对象上调用方法时,咱们会使用 `.`;而当咱们在指向某个对象的指针上调用方法,且需要首先解引用该指针时,咱们就要使用 `->`。换句话说,当 `object` 是个指针时,那么 `object->something()` 就类似于 `(*object).something()`。
|
||||
>
|
||||
> Rust 没有与 `->` 等价的运算符;相反,Rust 有一项称为 *自动引用和解引用,automatic referencing and dereferencing* 的特性。在 Rust 中,调用方法是少数几个具有这种行为的地方之一。
|
||||
> Rust 没有 `->` 的等价物;相反,Rust 有一项称为 *自动引用及解引用,automatic referencing and dereferencing* 的特性。调用方法属于 Rust 中有着这一行为的少数地方之一。
|
||||
>
|
||||
> 其工作原理如下:当咱们以 `object.something()` 调用某个方法时,Rust 会自动加入 `&`、`&mut` 或 `*`,以便 `object` 与该方法的签名相匹配。换句话说,下面两个方法是相同的:
|
||||
> 下面是其工作原理:当咱们以 `object.something()` 调用方法时,Rust 会自动添加 `&`、`&mut` 或 `*`,以便 `object` 与方法的签名匹配。换句话说,下面两行是相同的:
|
||||
>
|
||||
```rust
|
||||
p1.distance(&p2);
|
||||
(&p1).distance(&p2);
|
||||
```
|
||||
> ```rust
|
||||
> p1.distance(&p2);
|
||||
> (&p1).distance(&p2);
|
||||
> ```
|
||||
>
|
||||
> 第一种看起来更简洁。这种自动引用行为之所以有效,是因为方法有明确的接收者 -- 类型 `self`。有了方法的接收者和名字,Rust 就能明确确定,该方法是在读取 (`&self`)、改变 (`&mut self`) 还是消费 (`self`)。Rust 将方法的接收者,构造为隐式借用,这一事实,是在实践中,令到所有权符合人机工程学的重要部分。
|
||||
> 第一行看起来更简洁。这种自动引用行为之所以有效,是因为方法有个明确的接收者 -- 类型 `self`。给定方法的接收者和名字后,Rust 就可以明确地计算出该方法是在读取 (`&self`)、改变 (`&mut self`) 还是消费 (`self`)。Rust 将方法的接收者的借用构造为隐式这一事实,是令到所有权在实践中符合人机工程学的重要部分。
|
||||
|
||||
|
||||
## 带有更多参数的方法
|
||||
## 有着更多参数的方法
|
||||
|
||||
**Methods with More Parameters**
|
||||
|
||||
|
||||
我们来通过在 `Rectangle` 结构体上,实现第二个方法,来练习运用方法。这一次,我们希望 `Rectangle` 的实例,取另一 `Rectangle` 实例,在第二个 `Rectangle` 可以完全容纳在 `self`(第一个 `Rectangle`)中时,则返回 `true`;否则,返回 `false`。也就是说,只要我们已定义出 `can_hold` 方法,我们就可以编写下面清单 5-14 中所示的程序。
|
||||
我们来通过在 `Rectangle` 结构体上实现第二个方法练习使用方法。这次我们希望 `Rectangle` 实例取另一 `Rectangle` 实例,当第二个 `Rectangle` 可完全容纳在 `self`(第一个 `Rectangle`)内时返回 `true`;否则,他应返回 `false`。也就是说,一旦我们定义了 `can_hold` 方法,我们就希望能够编写出下面清单 5-14 中所示的程序。
|
||||
|
||||
|
||||
文件名:`src/main.rs`
|
||||
@@ -131,10 +128,10 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
*清单 5-14:使用尚未编写的 `can_hold` 方法*
|
||||
<a name="listing_5-14"></a>
|
||||
**清单 5-14**:使用尚未编写的 `can_hold` 方法
|
||||
|
||||
|
||||
由于 `rect2` 两个边都小于 `rect1` 的两个边,而 `rect3` 则宽于 `rect1`,因此预期输出结果如下:
|
||||
预期输出将如下,因为 `rect2` 两条边都小于 `rect1` 的两条边,而 `rect3` 宽于 `rect1`:
|
||||
|
||||
|
||||
```console
|
||||
@@ -142,7 +139,7 @@ rect1 可以容纳 rect2 吗?true
|
||||
rect1 可以容纳 rect3 吗?false
|
||||
```
|
||||
|
||||
我们知道咱们是要定义某个方法,因此其将位于那个 `impl Rectangle` 代码块中。方法的名称,将是 `can_hold`,同时他将取另一 `Rectangle` 的不可变借用作为参数。通过查看调用该方法的代码,我们可以判断出该参数的类型:`rect1.can_hold(&rect2)` 传入了 `&rect2`,其为对 `Rectangle` 实例 `rect2` 的不可变借用。这是有道理的,因为我们只需要读取 `rect2`(而不是写入,那意味着我们需要一个可变借用),而且我们希望 `main` 保留对 `rect2` 的所有权,这样我们就可以在调用 `can_hold` 方法后,再次使用他。`can_hold` 的返回值,将是个布尔值,其实现将检查 `self` 宽度和高度,是否分别大于另一 `Rectangle` 的宽度和高度。咱们来将这个新的 `can_hold` 方法,添加到清单 5-13 中的 `impl` 代码块中,如下清单 5-15 所示。
|
||||
我们知道咱们是要定义一个方法,因此他将位于 `impl Rectangle` 代码块内。方法的名字将是 `can_hold`,同时他将取另一 `Rectangle` 的不可变借用作为参数。通过查看调用该方法的代码我们可以区分这一参数的类型:`rect1.can_hold(&rect2)` 传入 `&rect2`,这是个对 `Rectangle` 实例 `rect2` 的不可变借用。这是有道理的,因为我们只需要读取 `rect2`(而不是写入,那将意味着我们需要可变借用),并且我们希望 `main` 保留对 `rect2` 的所有权,以便我们就可以在调用 `can_hold` 方法后再次使用他。`can_hold` 的返回值将是个布尔值,并其实现将分别检查 `self` 宽度和高度是否大于另一 `Rectangle` 的宽度和高度。咱们来添加这个新的 `can_hold` 方法到清单 5-13 中的 `impl` 代码块,如下清单 5-15 中所示。
|
||||
|
||||
|
||||
文件名:`src/main.rs`
|
||||
@@ -159,33 +156,27 @@ impl Rectangle {
|
||||
}
|
||||
}
|
||||
```
|
||||
<a name="listing_5-15"></a>
|
||||
**清单 5-15**:在 `Rectangle` 上实现 `can_hold` 方法,他会取另一 `Rectangle` 实例作为参数
|
||||
|
||||
*清单 5-15:在 `Rectangle` 上实现这个会取另一 `Rectangle` 实例作为参数的 `can_hold` 方法*
|
||||
|
||||
|
||||
当我们使用清单 5-14 中的 `main` 函数,运行这段代码时,就会得到我们想要的输出。方法可以取我们在 `self` 参数后,添加到签名的多个参数,而这些参数的作用,就跟函数中的参数一样。
|
||||
当我们以清单 5-14 中的 `main` 函数运行这段代码时,我们将得到我们想要的输出。方法可以取我们于 `self` 参数后添加到签名的多个参数,而这些参数会与函数中的参数一样生效。
|
||||
|
||||
|
||||
> **译注**:实际上,这个 `can_hold` 的实现有错误,因为其没有考虑到矩形倒转后可以容纳的情况,改进后的 `can_hold` 如下。
|
||||
|
||||
|
||||
```rust
|
||||
fn can_hold(&self, other: &Rectangle) -> bool {
|
||||
(self.width > other.width && self.height > other.height)
|
||||
|| (self.width > other.height && self.height > other.width)
|
||||
}
|
||||
|
||||
```
|
||||
>
|
||||
> ```rust
|
||||
> fn can_hold(&self, other: &Rectangle) -> bool {
|
||||
> (self.width > other.width && self.height > other.height)
|
||||
> || (self.width > other.height && self.height > other.width)
|
||||
> }
|
||||
> ```
|
||||
|
||||
|
||||
## 关联函数
|
||||
|
||||
**associated functions**
|
||||
定义在 `impl` 代码块内的所有函数都称为 *关联函数,associated function*,因为他们与 `impl` 后命名的类型相关联。我们可以定义不以 `self` 作为其第一个参数的关联函数(而因此不属于方法),因为他们不需要使用该类型的某个实例。我们已经用到过一个这样的函数:定义在 `String` 类型上的 `String::from` 函数。
|
||||
|
||||
|
||||
在 `impl` 代码块中定义的所有函数,都被称为 *关联函数,associated functions*,因为他们与 `impl` 后命名的那个类型相关联。我们可以定义不以 `self` 作为第一个参数的关联函数(而因此就不属于方法),因为他们不需要处理该类型的某个实例。我们已经使用过一个这样的函数:定义在 `String` 类型上的 `String::from` 函数。
|
||||
|
||||
非方法的关联函数,通常用于返回结构体新实例的构造函数。这些函数通常被称为 `new`,但 `new` 并不是个特殊的名字,也没有内置在语言中。例如,我们可以选择提供一个名为 `square` 的关联函数,该函数只有一个边长参数,并将其同时用作宽度和高度,这样就可以更轻松地创建一个正方形的 `Rectangle`,而不必两次指定同一个值两次:
|
||||
不属于方法的关联函数通常用于构造器,将返回结构体的新实例。这些通常叫做 `new`,但 `new` 并非一个特殊名字而未被内置在这门语言中。例如,我们可选择提供一个名为 `square` 的关联函数,该函数将只有一个边长参数,并将其同时用作宽度和高度,从而使创将正方形的 `Rectangle` 更容易,而不必指定同一个值两次:
|
||||
|
||||
|
||||
文件名:`src/main.rs`
|
||||
@@ -202,17 +193,14 @@ impl Rectangle {
|
||||
```
|
||||
|
||||
|
||||
返回类型和函数体中的两个 `Self` 关键字,是出现于 `impl` 关键字后那个类型的别名,在本例中即 `Rectangle`。
|
||||
这个函数中返回类型和函数体中的两个 `Self` 关键字,是在 `impl` 关键字后出现的类型的别名,在本例中即为 `Rectangle`。
|
||||
|
||||
要调用这个关联函数,我们要使用带有结构体名字的 `::` 语法;`let sq = Rectangle::square(3);` 就是个例子。该函数的命名空间为该结构体:`::` 这种语法,既用于关联函数,也用于由模块创建出的命名空间。我们将在 [第 7 章](../packages_crates_and_modules/defining_modules.md) 讨论模块。
|
||||
为了调用这个关联函数,我们要对结构体名字使用 `::` 语法;`let sq = Rectangle::square(3);` 就是个例子。这个函数被纳入到了该结构体的命名空间:`::` 语法同时用于关联函数,及由模组创建的命名空间。我们将在 [第 7 章](../packages_crates_and_modules/defining_modules.md) 中讨论模组。
|
||||
|
||||
|
||||
## 多个 `impl` 代码块
|
||||
|
||||
**Multiple `impl` Blocks**
|
||||
|
||||
|
||||
每个结构体,都可以有多个 `impl` 块。例如,清单 5-15 就相当于下面清单 5-16 中的代码,其中每个方法都有自己的 `impl` 块。
|
||||
每个结构体都允许有多个 `impl` 代码块。例如,[清单 5-15](#listing_5-15) 相当于下面清单 5-16 中的代码,其有着都在自己 `impl` 块中的各个方法。
|
||||
|
||||
|
||||
```rust
|
||||
@@ -230,18 +218,19 @@ impl Rectangle {
|
||||
}
|
||||
```
|
||||
|
||||
*清单 5-16:使用多个 `impl` 代码块重写清单 5-15*
|
||||
<a name="listing_5-16"></a>
|
||||
**清单 5-16**:使用多个 `impl` 代码块重写清单 5-15
|
||||
|
||||
|
||||
这里没有理由,将这些方法分隔成多个 `impl` 块,但这是有效的语法。在第 10 章讨论泛型和特质时,我们将看到多个 `impl` 代码块的用处。
|
||||
这里没有理由将这些方法分离为多个 `impl` 块,但这属于有效语法。我们将在第 10 章中我们讨论泛型与特质处,看到多个 `impl` 代码块很有用的情形。
|
||||
|
||||
|
||||
# 本章小结
|
||||
|
||||
|
||||
结构体可让咱们创建出,对咱们领域有意义的自定义类型。通过使用结构体,咱们可以将相关的数据片段,相互连接起来,并为每个片段命名,使咱们的代码清晰明了。在 `impl` 代码块中,咱们可以定义出,与咱们类型相关联的函数,而方法就是一种,可以让咱们指定出,结构体实例所具有行为的关联函数。
|
||||
结构体让咱们能够创建针对咱们领域有意义的自定义类型。通过使用结构体,咱们可以将相关的数据片段相互连接,并为每个片段命名以使咱们的代码清晰明了。在 `impl` 代码块中,咱们可以定义与咱们的类型关联的函数,而方法属于一种关联函数,让咱们可指定咱们的结构体实例所具有行为。
|
||||
|
||||
但是,结构体并不是咱们可以创建自定义类型的唯一方法:咱们来使用 Rust 的枚举特性,为咱们的工具箱添加另一种工具。
|
||||
但结构体并非咱们可创建自定义类型的唯一方式:咱们来转向 Rust 的枚举特性,添加另一种工具到咱们的工具箱。
|
||||
|
||||
|
||||
(End)
|
||||
|
||||
Reference in New Issue
Block a user