Update method.md

pull/193/head
Jesse 3 years ago committed by GitHub
parent 08d0757d82
commit ce4c96a526
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,14 +1,14 @@
# 方法Method # 方法Method
从面向对象语言过来的同学对于方法肯定不陌生,`class`里面就充斥着方法的概念在Rust中方法的概念也大差不差往往和对象成对出现: 从面向对象语言过来的同学对于方法肯定不陌生,`class` 里面就充斥着方法的概念。在Rust中方法的概念也大差不差往往和对象成对出现
```rust ```rust
object.method() object.method()
``` ```
例如读取一个文件写入缓冲区,如果用函数的写法`read(f,buffer)`,用方法的写法`f.read(buffer)`. 不过与其它语言`class`跟方法的联动使用不同Rust的方法往往跟结构体、枚举、特征一起使用特征将在后面几章进行介绍。 例如读取一个文件写入缓冲区,如果用函数的写法 `read(f,buffer)`,用方法的写法 `f.read(buffer)`。不过与其它语言 `class` 跟方法的联动使用不同(这里可能要修改下)Rust的方法往往跟结构体、枚举、特征Trait一起使用,特征(Trait)将在后面几章进行介绍。
## 定义方法 ## 定义方法
Rust使用`impl`来定义方法,例如以下代码: Rust使用`impl`来定义方法例如以下代码:
```rust ```rust
struct Circle { struct Circle {
x: f64, x: f64,
@ -34,7 +34,7 @@ impl Circle {
} }
``` ```
我们这里先不详细展开讲解,首先建立对方法定义的大致印象。下面图片将Rust方法定义与其它语言的方法定义做一下对比: 我们这里先不详细展开讲解,只是先建立对方法定义的大致印象。下面的图片将Rust方法定义与其它语言的方法定义做了对比:
<img alt="" src="/img/method-01.png" class="center"/> <img alt="" src="/img/method-01.png" class="center"/>
@ -64,11 +64,11 @@ fn main() {
} }
``` ```
该例子定义了一个`Rectangle`结构体,并且在其上定义一个`area`方法,用于计算该矩形的面积。 该例子定义了一个 `Rectangle` 结构体,并且在其上定义一个 `area` 方法,用于计算该矩形的面积。
`impl Rectangle {}`表示为`Rectangle`实现方法(`impl` 是实现*implementation* 的缩写),这样的写法标明`impl`语句块中的一切都是跟`Rectangle`相关联的。 `impl Rectangle {}` 表示为 `Rectangle` 实现方法(`impl` 是实现*implementation* 的缩写),这样的写法表明 `impl` 语句块中的一切都是跟 `Rectangle` 相关联的。
接下里的内容非常重要,请大家仔细看。在 `area` 的签名中,我们使用`&self`替代`rectangle: &Rectangle``&self`其实是`self: &Self`的简写(注意大小写)。在一个`impl`块内,`Self`指代被实现方法的结构体类型,`self`指代此类型的实例,换句话说,`self`指代的是`Rectangle`结构体实例,这样的写法会让我们的代码简洁很多,而且非常便于理解: 我们为哪个结构体实现方法,那么`self`就是指代的该结构体的实例。 接下里的内容非常重要,请大家仔细看。在 `area` 的签名中,我们使用 `&self` 替代 `rectangle: &Rectangle``&self` 其实是 `self: &Self` 的简写(注意大小写)。在一个 `impl` 块内,`Self` 指代被实现方法的结构体类型,`self` 指代此类型的实例,换句话说,`self` 指代的是 `Rectangle` 结构体实例,这样的写法会让我们的代码简洁很多,而且非常便于理解: 我们为哪个结构体实现方法,那么`self`就是指代哪个结构体的实例。
需要注意的是,`self` 依然有所有权的概念: 需要注意的是,`self` 依然有所有权的概念:
- `self` 表示 `Rectangle` 的所有权转移到该方法中,这种形式用的较少 - `self` 表示 `Rectangle` 的所有权转移到该方法中,这种形式用的较少
@ -77,7 +77,7 @@ fn main() {
总之,`self` 的使用就跟函数参数一样要严格遵守Rust的所有权规则。 总之,`self` 的使用就跟函数参数一样要严格遵守Rust的所有权规则。
回到上面的例子中,选择 `&self` 的理由跟在函数中使用 `&Rectangle` 是相同的:我们并不想获取所有权,也无需去改变它,只是希望能够读取结构体中的数据。如果想要在方法中去改变当前的结构体,需要将第一个参数改为 `&mut self`。通过仅仅使用 `self` 作为第一个参数来使方法获取实例的所有权是很少见的,这种使用方式往往用于把当前的对象转成另外一个对象时使用,转换完后,就不再关注之前的对象,且可以防止对之前对象的误调用。 回到上面的例子中,选择 `&self` 的理由跟在函数中使用 `&Rectangle` 是相同的:我们并不想获取所有权,也无需去改变它,只是希望能够读取结构体中的数据。如果想要在方法中去改变当前的结构体,需要将第一个参数改为 `&mut self`仅仅通过使用 `self` 作为第一个参数来使方法获取实例的所有权是很少见的,这种使用方式往往用于把当前的对象转成另外一个对象时使用,转换完后,就不再关注之前的对象,且可以防止对之前对象的误调用。
简单总结下,使用方法代替函数有以下好处: 简单总结下,使用方法代替函数有以下好处:
- 不用在函数签名中重复书写 `self` 对应的类型 - 不用在函数签名中重复书写 `self` 对应的类型
@ -104,7 +104,7 @@ fn main() {
} }
``` ```
当我们使用`rect1.width()`时Rust知道我们调用的是它的方法如果使用`rect1.witdh`,则是调用它的字段。 当我们使用 `rect1.width()` Rust知道我们调用的是它的方法如果使用 `rect1.witdh`,则是访问它的字段。
一般来说,方法跟字段同名,往往适用于实现 `getter` 访问器,例如: 一般来说,方法跟字段同名,往往适用于实现 `getter` 访问器,例如:
```rust ```rust
@ -129,7 +129,7 @@ fn main() {
} }
``` ```
用这种方式,我们可以把`Rectangle`的字段设置为私有属性,只需把它的`new`和`width`方法设置为公开可见,那么用户就可以创建一个矩形,同时通过访问器`rect1.width()`方法来获取矩形的宽度, 因为`width`字段是私有的,当用户访问`rect1.width`字段时,就会报错。注意在此例中,`Self`指代的就是被实现方法的结构体`Rectangle`。 用这种方式,我们可以把 `Rectangle` 的字段设置为私有属性,只需把它的`new`和`width`方法设置为公开可见,那么用户就可以创建一个矩形,同时通过访问器`rect1.width()` 方法来获取矩形的宽度,因为 `width` 字段是私有的,当用户访问 `rect1.width` 字段时,就会报错。注意在此例中,`Self` 指代的就是被实现方法的结构体 `Rectangle`
> ### `->` 运算符到哪去了? > ### `->` 运算符到哪去了?
> >
@ -163,7 +163,7 @@ fn main() {
> 第一行看起来简洁的多。这种自动引用的行为之所以有效,是因为方法有一个明确的接收者———— `self` 的类型。在给出接收者和方法名的前提下Rust 可以明确地计算出方法是仅仅读取(`&self`),做出修改(`&mut self`)或者是获取所有权(`self`。事实上Rust 对方法接收者的隐式借用让所有权在实践中更友好。 > 第一行看起来简洁的多。这种自动引用的行为之所以有效,是因为方法有一个明确的接收者———— `self` 的类型。在给出接收者和方法名的前提下Rust 可以明确地计算出方法是仅仅读取(`&self`),做出修改(`&mut self`)或者是获取所有权(`self`。事实上Rust 对方法接收者的隐式借用让所有权在实践中更友好。
## 带有多个参数的方法 ## 带有多个参数的方法
方法和函数一样,可以使用多个参数: 方法和函数一样,可以使用多个参数
```rust ```rust
impl Rectangle { impl Rectangle {
fn area(&self) -> u32 { fn area(&self) -> u32 {
@ -190,9 +190,9 @@ fn main() {
现在大家可以思考一个问题,如果为一个结构体定义一个构造器方法?也就是接受几个参数,然后构造并返回该结构体的实例。其实答案在开头的代码片段中就给出了,很简单,不使用`self`中即可。 现在大家可以思考一个问题,如果为一个结构体定义一个构造器方法?也就是接受几个参数,然后构造并返回该结构体的实例。其实答案在开头的代码片段中就给出了,很简单,不使用`self`中即可。
这种定义在`impl`中且没有`self`的函数被称之为**关联函数** 因为它没有`self`,不能用`f.read()`的形式使用,因此它是一个函数而不是方法,它又在`impl`中,与结构体紧密关联,因此称为关联函数。 这种定义在 `impl` 中且没有 `self` 的函数被称之为**关联函数** 因为它没有 `self`,不能用 `f.read()` 的形式调用,因此它是一个函数而不是方法,它又在`impl` 中,与结构体紧密关联,因此称为关联函数。
在之前的代码中,我们已经多次使用过关联函数,例如`String::from`,用于创建一个动态字符串。 在之前的代码中,我们已经多次使用过关联函数,例如`String::from`用于创建一个动态字符串。
```rust ```rust
# #[derive(Debug)] # #[derive(Debug)]
@ -238,7 +238,7 @@ impl Rectangle {
## 为枚举实现方法 ## 为枚举实现方法
枚举类型之所以强大,不仅仅在于它好用、可以[同一化类型](./compound-type/enum.md#同一化类型),还在于,我们可以像结构体一样,为枚举实现方法: 枚举类型之所以强大,不仅仅在于它好用、可以[同一化类型](./compound-type/enum.md#同一化类型),还在于,我们可以像结构体一样,为枚举实现方法
```rust ```rust
#![allow(unused)] #![allow(unused)]
@ -261,4 +261,4 @@ m.call();
} }
``` ```
除了结构体和枚举,我们还能为特征(trait)实现方法,将在下一章进行讲解,在此之前,先来看看泛型。 除了结构体和枚举,我们还能为特征(trait)实现方法,将在下一章进行讲解,在此之前,先来看看泛型。

Loading…
Cancel
Save