方法
方法与函数十分相似,它们都使用 fn 关键字及一个名称来进行声明,但是方法与函数依然是两个不同的概念,方法只能被定义在某个结构体、枚举类型、trait 对象的上下文中(后面会介绍枚举和 trait 对象),并且它们的第一个参数永远都是 self,用于指代调用该方法的结构体实例(可以理解为 JS 中的 this)。由于我们只接触过了结构体,所以本节只介绍结构体中的方法。
定义方法
定义一个结构体 Rect:
struct Rect {
width: u32,
height: u32,
}
复制代码
为结构体实现一个计算面积的方法 area,使用 impl 关键字后面跟结构体名称来作为实现方法的区域,impl 意为实现(implementation):
impl Rect {
// 用定义函数的方式定义一个方法
// 第一个参数作为对调用方法的结构体的引用
// self这种声明方式跟TS中this的类型声明很像
fn area(&self) -> u32 {
self.width * self.height
}
}
复制代码
使用方法:
let rect = Rect {
width: 100,
height: 100,
};
// 调用area方法,此时方法中的第一个参数&self表示&rect
println!("area:{}", rect.area());
// area:10000
复制代码
当使用 object.something()调用方法时,Rust 会自动为调用者 object 添加 &、&mut 或*,以使其能够符合方法的签名。换句话说,下面两种方法调用是等价的:
rect.area();
&rect.area();
复制代码
所以 rect 前边是否有 &或者 mut,是根据 area 方法第一个参数定义的时候对于 self 的定义推导出来的,例如自动将 rect 推断添加 mut:
impl Rect {
// 将第一个参数改为可变引用
fn area(&mut self) -> u32 {
self.height *= 2;
self.width * self.height
}
}
// 标识rect为可修改
let mut rect = Rect {
width: 100,
height: 100,
};
// 此时这两个是等价的,rust将自动将rect推断成&mut rect
println!("area:{}", rect.area()); // area:20000
println!("area:{}", &mut rect.area()); // area:20000
复制代码
方法也允许附带其他参数,例如下面比较矩形大小的方法:
impl Rect {
// 声明第二个参数为另一个引用类型的Rect
fn can_hold(&self, other: &Rect) -> bool {
self.width > other.width && self.height > other.height
}
}
// 定义三个rect
let rect1 = Rect { width: 50, height: 50 };
let rect2 = Rect { width: 10, height: 10 };
let rect3 = Rect { width: 60, height: 60 };
// 通过传参,比较rect1与其他两个的大小
println!("{}", rect1.can_hold(&rect2)); // true
println!("{}", rect1.can_hold(&rect3)); // false
复制代码
关联函数
在定义方法的时候,如果这个方法没有 self 参数,这样方法称之为关联函数,使用双冒号调用,例如我们用过的 String::from 方法:
为 Rect 定义一个关联函数,用于创建一个正方形:
impl Rect {
// 这里不用再定义self参数
fn square(size: u32) -> Rect {
Rect { width: size, height: size }
}
}
// 创建一个10 * 10的矩形
println!("{:?}", Rect::square(10));
// Rect { width: 10, height: 10 }
复制代码
多个 impl 块
当一个结构体的方法很多的时候,我们就需要将方法拆分到多个 impl 块中进行维护:
struct Rect {
width: u32,
height: u32,
}
impl Rect {
area(&self) -> u32 { /* ... */ }
}
impl Rect {
can_hold(&self, other: &Rect) -> bool { /* ... */ }
}
impl Rect {
square(size: u32) -> Rect { /* ... */ }
}
// 存在相同的方法名称报错:
impl Rect {
square() { /* ... */ } // 报错,重复的square方法定义
}
复制代码
评论