方法语法
方法类似于函数:我们使用fn
keyword 和
name,它们可以有参数和返回值,并且包含一些代码
当从其他位置调用方法时,将运行该方法。与函数不同,
方法在结构体(或枚举或 trait
object,我们将在第 6 章和第
17),它们的第一个参数是
总是self
,它表示方法正在被
召唤。
定义方法
让我们将area
函数具有Rectangle
实例作为参数
并改为创建一个area
在Rectangle
struct 中,如图所示
在示例 5-13 中。
#[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() ); }
area
方法上的Rectangle
结构要在Rectangle
,我们启动impl
(implementation) 块Rectangle
.此impl
块
将与Rectangle
类型。然后我们将area
功能
在impl
大括号并更改第一个(在本例中,仅更改)
parameter 设置为self
在签名中和正文中的任何地方。在main
,其中我们调用area
函数并传递rect1
作为参数,
我们可以改用方法语法来调用area
方法上的Rectangle
实例。方法语法在实例之后:我们添加一个点,后跟
方法名称、括号和任何参数。
在area
,我们使用&self
而不是rectangle: &Rectangle
.
这&self
实际上是self: &Self
.在impl
块中,该
类型Self
是impl
block 是 for 的。方法必须
具有一个名为self
的类型Self
对于它们的第一个参数,因此 Rust
允许您仅使用名称缩写此名称self
在第一个参数点。
注意,我们仍然需要在&
self
简写为
表示该方法借用了Self
实例,就像我们在rectangle: &Rectangle
.方法可以获得self
借self
不可变地,就像我们在这里所做的那样,或者借用self
可变的,就像他们可以将
other 参数。
我们选择了&self
这里的原因与我们使用的&Rectangle
在函数中
version:我们不想获得所有权,我们只想读取
结构,而不是写入它。如果我们想更改已
调用该方法作为该方法功能的一部分,我们将使用&mut self
如
第一个参数。具有一个通过以下方式获取实例所有权的方法
仅使用self
因为第一个参数很少见;这种技术通常是
在方法转换时使用self
转换为其他内容,并且您希望
阻止调用方在转换后使用原始实例。
使用方法而不是函数的主要原因,除了
提供方法语法,而不必重复self
在每个
method 的签名,用于组织。我们已经把我们能做的所有事情都投入了
将类型的实例放在一个impl
阻止而不是让未来的用户
的代码搜索功能Rectangle
在各个地方
我们提供的库。
请注意,我们可以选择为方法指定与结构体的
领域。例如,我们可以在Rectangle
也被命名为width
:
#[derive(Debug)] struct Rectangle { width: u32, height: u32, } 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); } }
在这里,我们选择将width
方法返回true
如果
实例的width
字段大于0
和false
如果值为0
:我们可以将同名方法中的字段用于任何目的。在main
,当我们跟随rect1.width
使用括号,Rust 知道我们指的是
方法width
.当我们不使用括号时,Rust 知道我们指的是字段width
.
通常(但并非总是)当我们为方法指定与所需的字段相同的名称时 it 只返回字段中的值,不执行任何其他作。像这样的方法 称为 getter,Rust 不会为 struct 自动实现它们 字段。getter 很有用,因为你可以将 field private 的 intent 的 intent 的 字段作为类型的公共 API 的一部分。我们将讨论哪些公共和私人 ,以及如何在章节中将字段或方法指定为 public 或 private 7.
作员在哪里?->
在 C 和 C++ 中,使用两种不同的运算符来调用方法:您可以使用.
如果你直接调用对象上的方法,并且你是
在指向对象的指针上调用该方法,并且需要取消引用
指针优先。换句话说,如果->
object
是一个指针,object->something()
类似于(*object).something()
.
Rust 没有等价于 operator 的 operator;相反,Rust 有一个
称为 自动引用 和 取消引用 的功能。调用方法是
Rust 中为数不多的具有此行为的地方之一。->
它的工作原理如下:当您使用object.something()
锈
自动添加 ,&
&mut
,或者*
object
匹配
方法。换句话说,以下内容是相同的:
#![allow(unused)] fn main() { #[derive(Debug,Copy,Clone)] struct Point { x: f64, y: f64, } impl Point { fn distance(&self, other: &Point) -> f64 { let x_squared = f64::powi(other.x - self.x, 2); let y_squared = f64::powi(other.y - self.y, 2); f64::sqrt(x_squared + y_squared) } } let p1 = Point { x: 0.0, y: 0.0 }; let p2 = Point { x: 5.0, y: 6.5 }; p1.distance(&p2); (&p1).distance(&p2); }
第一个看起来更干净。此自动引用行为有效
因为方法有一个明确的接收器 - 类型的self
.给定接收器
和方法的名称,Rust 可以明确地判断该方法是否是
阅读 (&self
)、突变 (&mut self
) 或消耗 (self
).事实
Rust 将 Borrow 隐式用于方法接收器是
在实践中使所有权符合人体工程学。
具有更多参数的方法
让我们通过在Rectangle
结构。这一次,我们想要一个Rectangle
获取另一个实例
之Rectangle
并返回true
如果第二个Rectangle
可以完全贴合
在self
(第一个Rectangle
);否则,它应该返回false
.
也就是说,一旦我们定义了can_hold
方法,我们希望能够编写
程序如图 5-14 所示。
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));
}
can_hold
方法预期输出将如下所示,因为rect2
小于rect1
但rect3
宽于rect1
:
Can rect1 hold rect2? true
Can rect1 hold rect3? false
我们知道我们想要定义一个方法,所以它将在impl Rectangle
块。方法名称将为can_hold
,并且需要 immutable borrow
另一个Rectangle
作为参数。我们可以看出
parameter 将通过查看调用该方法的代码来执行:rect1.can_hold(&rect2)
传入&rect2
,这是对rect2
、Rectangle
.这是有道理的,因为我们只需要
读rect2
(而不是 write,这意味着我们需要一个可变的借用),
我们想要main
保留rect2
这样我们就可以在
调用can_hold
方法。的返回值can_hold
将是一个
Boolean 的 Boolean 中,实现会检查self
大于另一个Rectangle
,
分别。让我们添加新的can_hold
方法添加到impl
block from (阻止)
示例 5-13,如示例 5-15 所示。
#[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 } } 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)); }
can_hold
method 开启Rectangle
那需要另一个Rectangle
实例作为参数当我们使用main
函数,我们将得到
期望的输出。方法可以接受我们添加到
签名self
parameter 的 Parameter 一样,这些参数的工作方式就像
functions 中的 parameters 进行
关联功能
在impl
块称为关联函数,因为它们与以impl
.我们可以定义
没有self
作为其第一个参数(因此
不是方法),因为它们不需要该类型的实例即可使用。
我们已经使用了一个函数,如下所示:String::from
函数,即
在String
类型。
不是方法的关联函数通常用于满足以下条件的构造函数
将返回结构体的新实例。这些通常称为new
但new
不是一个特殊名称,也不内置于语言中。例如,我们
可以选择提供一个名为square
那会
一个维度参数,并将其用作宽度和高度,从而使其
更容易创建正方形Rectangle
而不必指定相同的
value 的 2 倍:
文件名: src/main.rs
#[derive(Debug)] struct Rectangle { width: u32, height: u32, } impl Rectangle { fn square(size: u32) -> Self { Self { width: size, height: size, } } } fn main() { let sq = Rectangle::square(3); }
这Self
返回类型和函数体中的关键字是
别名,用于显示在impl
keyword 的
是Rectangle
.
要调用这个关联的函数,我们使用::
语法;let sq = Rectangle::square(3);
就是一个例子。此函数的命名空间为
结构体:该::
语法用于关联的函数和
由模块创建的命名空间。我们将在 Chapter 中讨论模块
7.
倍数impl
块
每个结构体都可以有多个impl
块。例如,Listing
5-15 相当于示例 5-16 中所示的代码,它的每个方法都在
自己impl
块。
#[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 } } 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)); }
impl
块没有理由将这些方法分成多个impl
块中,
但这是有效的语法。我们将看到一个情况,其中多个impl
块是
在第 10 章中很有用,我们在这里讨论泛型类型和 trait。
总结
结构允许您创建对您的域有意义的自定义类型。由
使用结构体,您可以保持关联的数据片段彼此连接
并为每个部分命名以使您的代码清晰。在impl
块中,您可以定义
函数,而 methods 是一种
关联的函数,该函数允许您指定
structs 有。
但是结构并不是创建自定义类型的唯一方法:让我们转向 Rust 的 enum 功能将另一个工具添加到你的工具箱中。
本文档由官方文档翻译而来,如有差异请以官方英文文档(https://doc.rust-lang.org/)为准