5分钟速读之Rust权威指南(八)

201 阅读3分钟

方法

方法与函数十分相似,它们都使用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方法:

String::from("hello");

为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方法定义
}