虽然我不喜欢扣字眼,但在Rust中,方法一般指依附于结构体的函数,它需要在结构体之上调用。而函数一般指独立的功能,可以自主运行。
方法
方法一般需要依附于结构体,方法通过关键字 self
来访问对象中的数据和其他。方法在 impl
代码块中定义。
静态方法 static method
静态方法不需要实例来调用,把结构体看作Class,静态方法则可以直接在Class上调用。得益于此特性,静态方法一般用于构造函数,返回自己的实例。
struct Point {
x: f64,
y: f64,
}
// 实现的代码块,`Point` 的所有方法都在这里给出
impl Point {
// 这是一个静态方法(static method)
// 静态方法不需要被实例调用
// 这类方法一般用作构造器(constructor)
// 构造一个初始值的实例
fn origin() -> Point {
Point { x: 0.0, y: 0.0 }
}
// 另外一个静态方法,构造任意的实例
// 需要两个参数:
fn new(x: f64, y: f64) -> Point {
Point { x: x, y: y }
}
}
实例方法 instance method
实例方法需要依附于实例调用,因此一般的实例方法的第一个参数都是 self
。常见的有:
&self
,这是self: &Self
的语法糖&mut self
,这是self: &mut Self
的语法糖self
,这是self: Self
的语法糖,会把实例的所有权从父context中转移到方法中,如果不返回内容,则直接销毁。
struct Rectangle {
p1: Point,
p2: Point,
}
impl Rectangle {
// 这是一个实例方法(instance method)
// `&self` 是 `self: &Self` 的语法糖(sugar),其中 `Self` 是方法调用者的
// 类型。在这个例子中 `Self` = `Rectangle`
fn area(&self) -> f64 {
// `self` 通过点运算符来访问结构体字段
let Point { x: x1, y: y1 } = self.p1;
let Point { x: x2, y: y2 } = self.p2;
// `abs` 是一个 `f64` 类型的方法,返回调用者的绝对值
((x1 - x2) * (y1 - y2)).abs()
}
fn perimeter(&self) -> f64 {
let Point { x: x1, y: y1 } = self.p1;
let Point { x: x2, y: y2 } = self.p2;
2.0 * ((x1 - x2).abs() + (y1 - y2).abs())
}
// 这个方法要求调用者是可变的
// `&mut self` 为 `self: &mut Self` 的语法糖
fn translate(&mut self, x: f64, y: f64) {
self.p1.x += x;
self.p2.x += x;
self.p1.y += y;
self.p2.y += y;
}
}
// `Pair` 拥有资源:两个堆分配的整型
struct Pair(Box<i32>, Box<i32>);
impl Pair {
// 这个方法会 “消耗” 调用者的资源
// `self` 为 `self: Self` 的语法糖
fn destroy(self) {
// 解构 `self`
let Pair(first, second) = self;
println!("Destroying Pair({}, {})", first, second);
// `first` 和 `second` 离开作用域后释放
}
}
测试一下
要注意的点:
- 静态方法使用类名 + 双冒号调用
- 实例方法通过点运算符来调用
fn main() {
let rectangle = Rectangle {
// 静态方法使用双冒号调用
// 分别构造了两个实例p1和p2
p1: Point::origin(),
p2: Point::new(3.0, 4.0),
};
// 实例方法通过点运算符来调用
// 注意第一个参数 `&self` 是隐式传递的,亦即:
// `rectangle.perimeter()` === `Rectangle::perimeter(&rectangle)`
println!("Rectangle perimeter: {}", rectangle.perimeter());
println!("Rectangle area: {}", rectangle.area());
let mut square = Rectangle {
p1: Point::origin(),
p2: Point::new(1.0, 1.0),
};
// 报错! `rectangle` 是不可变的,但这方法需要一个可变对象
// rectangle.translate(1.0, 0.0);
// 试一试 ^ 去掉此行的注释
// 正常运行!可变对象可以调用可变方法 -> &mut self
square.translate(1.0, 1.0);
let pair = Pair(Box::new(1), Box::new(2));
pair.destroy(); // 这里直接转移了所有权,并在destory中销毁了
// 报错!前面的 `destroy` 调用 “消耗了” `pair`
//pair.destroy();
// 试一试 ^ 将此行注释去掉
// 这里所以就调用不了了
}