Rust - method

196 阅读4分钟

这是我参与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 块(implimplementation 的缩写),每个结构体都允许有多个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的枚举功能同样可以为开发人员的工具箱再添加一个工具。

结语

文章首发于微信公众号程序媛小庄,同步于掘金

码字不易,转载请说明出处,走过路过的小伙伴们伸出可爱的小指头点个赞再走吧(╹▽╹)