mirror of
https://github.com/gnu4cn/rust-lang-zh_CN.git
synced 2025-03-14 19:30:29 +08:00
Refining Ch05.
This commit is contained in:
parent
da1d65a5d8
commit
e1efe9d86b
@ -4,14 +4,21 @@ struct Rectangle {
|
||||
height: u32,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let scale = 2;
|
||||
|
||||
let rect1 = Rectangle {
|
||||
width: dbg! (30 * scale),
|
||||
height: 50,
|
||||
};
|
||||
|
||||
dbg! (&rect1);
|
||||
impl Rectangle {
|
||||
fn area(&self) -> u32 {
|
||||
self.width * self.height
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let rect1 = Rectangle {
|
||||
width: 30,
|
||||
height: 50,
|
||||
}
|
||||
|
||||
println! (
|
||||
"该矩形的面积为 {} 平方像素。"
|
||||
rect1.area()
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,12 @@ struct User {
|
||||
sign_in_count: u64,
|
||||
}
|
||||
|
||||
impl Rectangle {
|
||||
fn area(&self) -> u32 {
|
||||
self.width * self.height
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let user1 = User {
|
||||
active: true,
|
||||
|
@ -2,7 +2,8 @@
|
||||
|
||||
**Method Syntax**
|
||||
|
||||
*方法* 与函数类似:是以 `fn` 关键字和一个名称,来声明出方法,方法可以有参数和返回值,同时包含了在某个地方方法被调用时,运行的一些代码。与函数不同的地方在于,方法是在结构体(或者枚举或特质对象,关于枚举即特质对象,将分别在第 6 和 17 章讲到)的语境里面定义的,且方法的首个参数将始终是 `self`,这个 `self` 表示方法被调用的那个结构体实例本身。
|
||||
|
||||
*方法* 与函数类似:我们用 `fn` 关键字和一个名字,来声明方法,方法可以有参数和返回值,同时他们还包含一些代码,这些代码会在从其他地方调用该方法时运行。与函数不同的是,方法是在某个结构体(或某个枚举或特质对象,我们将在 [第 6 章](../Ch06_Enums_and_Pattern_Matching.md) 和 [第 17 章](../oop/trait_objects.md) 分别介绍)的上下文中定义的,而且方法的第一个参数,总是表示该方法被调用所在的结构体实例本身的 `self`。
|
||||
|
||||
|
||||
## 定义出方法
|
||||
@ -10,7 +11,8 @@
|
||||
**Defining Methods**
|
||||
|
||||
|
||||
下面就来将那个将一个 `Rectangle` 实例作为参数的 `area` 函数,修改为定义在 `Rectangle` 结构体上的 `area` 方法,如下清单 5-13 所示:
|
||||
如下清单 5-13 所示,我们来修改以 `Rectangle` 实例为参数的那个 `area` 函数,转而构造出一个定义在 `Rectangle` 结构上的 `area` 方法。
|
||||
|
||||
|
||||
```rust
|
||||
#[derive(Debug)]
|
||||
@ -40,17 +42,20 @@ fn main() {
|
||||
*清单 5-13:在 `Rectangle` 结构体上定义一个 `area` 方法*
|
||||
|
||||
|
||||
为定义 `Rectangle` 上下文中的函数,这里开启了一个 `Rectangle ` 的 `impl` (implementation)代码块。此 `impl` 代码块里头的所有物件,都会与那个 `Rectangle` 类型相关联。随后这里就把原来那个 `area` 函数,移入到这个 `impl` 的花括弧里,并将函数签名中的首个(而在此情形下,也是唯一的)参数,及函数体中的各处,均修改为 `self`。在 `main` 函数,即原先调用 `area` 函数与将 `rect1` 作为参数传递的地方,现在就可以使用 *方法语法* 来调用那个在 `Rectangle` 实例上的 `area` 方法了。方法语法(the method syntax)是在实例之后:添加一个带着方法名字、括号及全部参数的点。
|
||||
为了在 `Rectangle` 上下文中定义这个函数,我们为 `Rectangle` 创建了一个 `impl`(implementation,实现)代码块。该 `impl` 代码块中的所有内容,都将与 `Rectangle` 这个类型相关联。然后,我们将那个 `area` 函数,移入 `impl` 的花括号中,并把函数签名中的首个(且本示例中唯一的)参数修改为 `self`, 同时修改函数体中的各处。在 `main` 中,在我们曾调用过 `area` 函数并将 `rect1` 作为参数传递的地方,我们便可以使用 *方法语法,method syntax*,在咱们的 `Rectangle` 实例上,调用 `area` 方法。方法语法位于某个实例之后:我们要添加一个后跟方法名称、圆括号和任何参数的一个点。
|
||||
|
||||
在 `area` 的签名中,使用了 `&self` 而不再是 `rectangle: &Rectangle`。这个 `&self` 实际上是 `self: &Self` 的简写。在 `impl` 代码块内部,类型 `Self` 就是该 `impl` 代码块所针对的类型。方法必定有着这么一个名为 `self` 类型为 `Self` 的参数,作为他们的首个参数,因此 Rust 这才允许将首个参数位置上的该参数,简写为只是他的名称 `self`。请注意这里仍然需要在 `self` 简写前使用 `&` 运算符,来表示此方法借用了 `Self` 类型的实例,这就跟 `rectangle: &Rectangle` 一样。方法可以取得 `self` 的所有权的、不可变地借用 `self` 变量,或者可变地借用 `self` 变量,对于方法的其他参数,也是这样的。
|
||||
在 `area` 的签名中,我们使用了 `&self` 而不是 `rectangle: &Rectangle`。`&self` 实际上是 `self:&Self` 的缩写。在 `impl` 代码块中,`Self` 这个类型,是 `impl` 代码块,所针对的那个类型的别名。方法必须将名为 `self`,类型为 `Self` 的参数,作为其第一个参数,因此 Rust 允许咱们,在第一个参数处,将其缩写为仅 `self` 这个名字。请注意,就像在 `rectangle: &Rectangle` 中一样,我们仍然需要在 `self` 这个简写前面,使用 `&` 来表明该方法借用了 `Self` 这个实例。方法可以取得 `self` 的所有权,也可以不可变地借用 `self`(就像我们在这里所做的),还可以可变地借用 `self`(就像借用其他参数一样)。
|
||||
|
||||
> `&self` - 不可变借用;`&mut self` 可变借用;`self` - 取得所有权,发生所有权转移,`self` 所指向的内存堆上的值原来的所有值将失效。
|
||||
|
||||
这里选择了 `&self`,有着与方法版本中使用 `&Rectangle` 有着同样理由:那就是不打算取得所有权,同时只打算读取结构体中的数据,而不打算写入。在作为方法要执行的一部分,要修改方法调用所在实例时,就要使用 `&mut self` 作为首个参数了。通过仅使用 `self` 作为首个参数,而取得实例所有权的情况,就非常少见了;通常在要将 `self` 转换为其他类型的数据,而要在这样的转换之后,阻止其他调用者使用原先的实例时,会用到这样的技巧。
|
||||
> **译注**:`&self` - 不可变借用;`&mut self` 可变借用;`self` - 取得所有权,发生所有权转移,`self` 所指向的内存堆上值,原先那个在栈上的变量将失效。
|
||||
|
||||
使用方法而不是函数的主要原因,除了提供到方法语法及不必在每个方法签名中重复 `self` 的类型外,那就是为了代码的组织了。这里已将可由某个类型实例完成的事情,放在一个 `impl` 代码块中,而不是要那些后来的代码使用者,在这里提供的库的各个地方去找寻 `Rectangle` 的那些能力。
|
||||
|
||||
请注意可选择给方法一个与结构体字段相同的名字。比如,这里就可以在 `Rectangle` 上定义一个同样命名为 `width` 的方法:
|
||||
我们在这里选择 `&self` 的原因,与我们在函数那个版本中使用 `&Rectangle` 的原因相同:我们不打算取得所有权,我们只想读取该结构体中的数据,而不是向其写数据。如果我们打算修改调用方法的实例(作为该方法的一部分),我们可以使用 `&mut self` 作为第一个参数。只使用 `self` 作为第一个参数,来取得实例所有权的方法并不多见;这种方法通常用于该方法会将 `self` 转换成其他东西,且咱们想要防止调用者,在这种转换后继续使用原始实例的时候。
|
||||
|
||||
除了提供方法语法和不必在每个方法的签名中,重复 `self` 的类型外,使用方法而不是函数的主要原因,是为了组织。我们把所有能用类型实例做的事情,都放在一个 `impl` 块中,而不是让我们代码的未来用户,在我们提供的库中不同地方,检索 `Rectangle` 的功能。
|
||||
|
||||
请注意,我们可以选择将某个方法,命名为与结构体的某个字段同名。例如,我们可以为 `Rectangle` 定义一个名为 `width` 的方法:
|
||||
|
||||
|
||||
文件名:`src/main.rs`
|
||||
|
||||
@ -68,18 +73,21 @@ fn main() {
|
||||
};
|
||||
|
||||
if rect1.width() {
|
||||
println! ("该矩形的宽不为零;他的宽为 {}", rect1.width);
|
||||
println! ("该矩形的宽度不为零;其为 {}", rect1.width);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
这里,就选择了让 `width` 方法,在实例的 `width` 字段中的值大于 `0` 时返回 `true`,在值为 `0` 时返回 `false`:在名称与某个字段相同的方法里面,可将该字段用于任何目的。在 `main` 方法中,当这里在 `rect1.width` 后跟上一对圆括号时,那么 Rust 就明白这里指的是方法 `width`了。而在没有使用一对圆括号时,Rust 就知道那里表示的是字段 `width`。
|
||||
|
||||
通常,但并非总是这样,在给到方法与某个字段同样名字时,要的是让那个方法返回与其同名字段中的值,而不会去干别的事情。像这样的方法,就叫做 *获取器(getters)*,而 Rust 并未像其他语言所做的那样,自动实现结构体字段的获取器。由于可将字段构造为私有,而将方法构造为公开,而由此实现对作为类型的公开 API一部分的字段的只读访问。在第 7 章中就会讨论到何为公开与私有,以及怎样将字段或方法指定为公开或私有。
|
||||
在这里,我们选择将这个 `width` 方法,构造为在实例的 `width` 字段中值大于 `0` 时,返回 `true`;如果该值为 `0`,则返回 `false`:我们可以在与某个字段的同名方法中,使用这个字段来达到任何目的。在 `main` 中,当我们在 `rect1.width` 后加上括号时,Rust 就知道我们指的是 `width` 这个方法。当我们不使用括号时,Rust 知道我们指的是 `width` 字段。
|
||||
|
||||
> **`->` 操作符(the `->` operator)哪去了呢?**
|
||||
通常(但不总是),当我把某个方法,命名为与一个字段同名时,我们就希望他只返回字段中的值,而不做其他任何事情。这样的方法称为 *获取器,getter*,而 Rust 并未像其他语言那样,自动为结构的字段实现获取器。获取器之所以有用,是因为我们可以将该字段,构造为私有,而将该方法构造为公开,从而将对该字段的只读访问,实现为该类型公开 API 的一部分。我们将在 [第 7 章](../packages_crates_and_modules/paths.md#使用-pub-关键字对路径进行暴露) 讨论什么是公开,public 和私有,private,以及如何将字段或方法,指定为公开或私有。
|
||||
|
||||
|
||||
> **`->` 操作符在哪里?**
|
||||
>
|
||||
> 在 C 和 C++ 中,有两种不同的操作符用于调用方法:如果是直接调用对象上的方法,咱们会使用 `.`;如果是调用到对象的某个指针上的方法,并且需要首先解引用该指针时,则要使用 `->`。换句话说,如果 `object` 是个指针,则 `object->something()` 类似于 `(*object).something()`。
|
||||
>
|
||||
> 在 C 和 C++ 中,方法调用会用到两个操作符:直接调用在对象上的方法时,要用到 `.`,而在对象的指针上调用方法时,则要用 `->` 操作符,这时还先要对该指针解除引用。换句话说,在 `object` 是个指针时,`object -> something()` 是类似于 `(*object) -> something()` 的。
|
||||
> Rust 并无 `->` 操作符的等价操作符;相反,Rust 有着一项名为 *自动引用与解引用(automatic referencing and dereferencing)* 的特性。而方法调用就是 Rust 中有着这种行为表现的少数几个地方之一。
|
||||
>
|
||||
> 以下就是该特性的工作原理:在以 `object.something()` 调用某个方法时,Rust 会自动加上 `&`、`&mut` 或 `*`,这样 `object` 就会匹配上该方法的签名。换句话说,下面的语句是一致的:
|
||||
|
Loading…
Reference in New Issue
Block a user