在这里插入图片描述

在其他语言里,我们通常不会特别区分“函数”和“方法”两个术语,特别是在 Java 这类纯面向对象编程语言里。因为“函数”和“方法”是一回事。在 C++ 里,情形稍有不同,因为它是面向对象和面向过程的多范式语言,即有独立存在的函数,也有位于类里的“成员函数”,是的, C++ 管类里的函数叫“成员函数”

在 Rust 里情况又有所不同,它有的独立的函数 (function),同时,还有关联到 struct / trait / enum 上的函数,Rust 把它们称之为:方法 (method),暗示它们是从属于这些数据结构的。所以,严谨一些的话,在 Rust 里还是不要随意交叉使用这两个术语。下面我们看一下如何给一个结构体添加方法。假设有一个 Rectangle 结构体:

struct Rectangle {
    width: u32,
    height: u32,
}

下面我们要为其添加一下求解面积的方法,应该这样写:

impl Rectangle {
    // 注意参数 &self
    fn area(&self) -> u32 {
        self.width * self.height
    }
}

定义好这个方法后,我们就能通过 Rectangle 的实例直接调用它了:

fn main() {
    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };

    println!("rect1.area() == {}", rect1.area());
}

首先是关键字 impl,它后面紧跟一个类型,可以是 struct / trait / enum,然后用花括号包裹起的部分就称为一个 impl 块(impl block),这个语法形式挺简洁易懂的,仅从效果上看,它是要给指定的 struct / trait / enum 添加一些“方法”。不过,对于 rust 中的 struct / enum 来说,它们并没有真正意义上的“方法”,impl 实现的其实是:关联到类型上的普通函数集合。怎么理解呢?我们看一下使用 impl 定义的这些方法的真实情况。

你应该注意到 area 方法的特殊之处:它有一个 &self 参数,这个 self 代表的就是调用这个方法的 struct 实例,而根据方法的意图和功能可以使用 &self / &mut self / self 等不同形式来控制所有权的授予。Rust 规定 impl 中的方法第一个参数必须是 &self / &mut self / self (关联函数除外),这种语法形式并不陌生,这和 C++ 的成员函数总是额外获得一个隐式的 this 指针以方法对象的其他成员非常类似。但是,有一个细节需要我们注意:尽管在方法声明中我们可以根据需要写 &self / &mut self / self 等不同形式,但是你会发现,像 rect1.area() 这样在调用时,我们是不会显式传递第一个参数的,从使用者的角度来说,这很好,非常方便,但是,你要注意到,不管 area 方法声明的是 &self、&mut self 还是 self,调用形式是一样的,都是 rect1.area(),所以,这里有一个细节就是:Rust 会自动为 object 添加 &、&mut 或 *(解引用) 以便使 object 与方法签名匹配。

现在回看 rect1.area(),你就能感觉到 Rust 通过 impl 定义的方法与它关联的结构体其实是一种很松散的绑定关系,rect1.area() 的调用更像是 area(&rect1) 形式的语法糖。所以 struct 是没有真正意义上的“成员函数”的。

最后,还有一个前面一直没解释的“例外”:关联函数,它就是在定义在了 impl 块里,但第一个参数不是 self 的任何形式的方法,这样,它就不会作用到一个具体的结构体实例上了,这和其他语言中的静态成员函数是一样的。关联函数经常被用作返回一个结构体新实例的构造函数/工厂方法,例如,我们可以 Rectangle 添加一个 square 关联方法,作为创建正方形的一种快捷方式:

impl Rectangle {
    // 无 &self 参数,是一个 关联函数
    fn square(size: u32) -> Rectangle {
        Rectangle {
            width: size,
            height: size,
        }
    }
}

更多推荐