这是我参与11月更文挑战的第29天,活动详情查看:2021最后一次更文挑战
方法与函数类似,也使用 fn
关键字和名称声明,可以拥有参数和返回值,同时包含在某处调用该方法时会执行的代码。不过方法与函数是不同的,因为它们在结构体的上下文中被定义(或者是枚举或 trait 对象的上下文),方法的第一个参数总是 self
,它代表调用该方法的结构体实例。
定义方法
在上一篇文章中提到的WidthHeight
结构体为例,将计算长方形面积的函数改写成一个定义于WidthHeight
结构体上的area
方法。代码示例如下:
struct WidthHeight {
width: u32,
height: u32,
}
impl WidthHeight {
fn area(&self) -> u32 {
self.width * self.height
}
}
fn main() {
let tup_w_h = WidthHeight {width: 10, height: 20}; // 索引0对应宽度
println!("长方形的面积是{}", tup_w_h.area())
}
为了让函数定义于WidthHeight
结构体的上下文中,需要使用 impl
块(impl
是 implementation 的缩写),每个结构体都允许有多个impl
块,然后接着将 area
函数移动到 impl
大括号中,并将签名中的第一个(在这里也是唯一一个)参数和函数体中其他地方的对应参数改成 self
。然后在 main
中使用 方法语法(method syntax)在 WidthHeight
实例上调用 area
方法。
在 area
的签名中,使用 &self
来替代 width_height: &WidthHeight
,因为该方法位于 impl Rectangle
上下文中所以 Rust 知道 self
的类型是 WidthHeight
。注意仍然需要在 self
前面加上 &
,就像 &WidthHeight
一样。方法可以选择获取 self
的所有权,或者像这里一样不可变地借用 self
,或者可变地借用 self
,就跟其他参数一样。
这里选择 &self
的理由跟在函数版本中使用 &WidthHeight
是相同的:我们并不想获取所有权,只希望能够读取结构体中的数据,而不是写入。如果想要在方法中改变调用方法的实例,需要将第一个参数改为 &mut self
。通过仅仅使用 self
作为第一个参数来使方法获取实例的所有权是很少见的;这种技术通常用在当方法将 self
转换成别的实例的时候,这时我们想要防止调用者在转换之后使用原始的实例。
使用方法替代函数,除了可使用方法语法和不需要在每个函数签名中重复 self
的类型之外,其主要好处在于组织性。我们将某个类型实例能做的所有事情都一起放入 impl
块中,而不是让将来的用户在我们的库中到处寻找 WidthHeight
的功能。
定义带有多个参数的方法
基于WidthHeight
结构体,有一个新的需求,让WidthHeight
实例和另一个WidthHeight
实例进行比较,如果当前实例能够完全包含第二个实例时返回true
,否则返回false
。
根据上面的需求,我们应该在WidthHeight
结构体中再定义一个方法,用于比较两个长方形的大小,并且该方法会获取另一个 WidthHeight
实例 的不可变借用作为参数。代码示例如下:
struct WidthHeight {
width: u32,
height: u32,
}
impl WidthHeight {
fn compare(&self, other: &WidthHeight) -> bool {
self.width > other.width && self.height > other.height
}
}
fn main() {
let area1 = WidthHeight {width: 10, height: 20};
let area2 = WidthHeight {width: 15, height: 22};
println!("area1是否能够包含area2? {}", area1.compare(&area2));
}
关联函数
impl
块的另一个有用的功能是:允许在 impl
块中定义 不 以 self
作为参数的函数。这被称为 关联函数(associated functions),因为它们与结构体相关联。它们仍是函数而不是方法,因为它们并不作用于一个结构体的实例。
关联函数经常被用作返回一个结构体新实例的构造函数。例如可以提供一个关联函数,它接受一个维度参数并且同时作为宽和高,这样可以更轻松的创建一个正方形 Rectangle
而不必指定两次同样的值:
impl WidthHeight {
fn square(size: u32) -> Rectangle {
Rectangle { width: size, height: size }
}
}
总结
通过结构体,我们可以将相关联的数据片段联系起来并命名它们,这样可以使得代码更加清晰。方法允许为结构体实例指定行为,而关联函数将特定功能置于结构体的命名空间中并且无需一个实例。
但结构体并不是创建自定义类型的唯一方法,Rust的枚举功能同样可以为开发人员的工具箱再添加一个工具。
结语
文章首发于微信公众号程序媛小庄,同步于掘金。
码字不易,转载请说明出处,走过路过的小伙伴们伸出可爱的小指头点个赞再走吧(╹▽╹)