面向对象的类class中就充斥着方法的概念 在Rust中,往往和对象成对出现:

object.method()

定义方法

使用impl来定义方法:

struct Circle{
	x:f64,
	y:f64,
	radius:f64,
}
impl Circle{
	//new是Circle的关联函数,因为它的第一个参数不是self,且new并不是关键字
	fn new(x:f64,y:f64,radius:f64)->Circle{
		Circle{
			x:x,
			y:y,
			radius:radius,
		}
	}
	//Circle的方法,&self表示借用当前的Circle结构体
	fn area(&self)->f64{
		std::std::consts::PI*(self.radius*self.radius)
	}
}

Rust的方法定义和其他语言的区别:

Pasted image 20261010171938.png

Rust的对象定义和方法定义是分离的,给予了使用者极高的灵活度

#[derive(Debug)]
struct Rectangle{
	width:u32,
	height:u32,
}

impl Rectangle{
	fn area(&self)->u32{
		self.width*self.height
	}
}
fn main(){
	let rect1 = Rectangle{width:30,height:50};
	
	println!("The area of the rectangle is {} square pixels.",rect1.area());
}

上面定义了一个Rectangle结构体,并定义了一个area方法,用于计算该矩形的面积 impl Rectangle{}表示为Rectangle实现方法,(impl是实现implement的缩写),这样的写法表明impl中的一切都是和Rectangle相关的

self,&self和&mut self

在上面area的签名中,我们使用&self代替rectangle:Rectangle,&self其实是self:&Self的简写,在一个impl块中,Self指代被实现方式的结构体类型,self指代这个类型的实例,换句话说self指代的是Rectangle结构体实例,这样的写法会让我们的代码简洁很多,而且很容易理解:我们为哪个结构体实现方法,那么self就是指代哪个结构体的实例; self依然有所有权的概念:

  • self表示Rectangle的所有权转移到该方法中,这种形式用的较少
  • &self表示该方法对Rectangle的不可变调用
  • &mut self表示可变调用 回到上面的例子中,选择&self的理由在函数中使用&Rectangle是相同的:我们并不想获取所有权,也无需去改变它,只是希望能够读取结构体中的数据。 要想在方法中改变当前的结构体,需要将第一个参数设置为&mut self,仅仅通过使用self作为第一个参数来使方法获取实例的所有权是很少见的,这种方式往往用于将当前的对象转为另一个对象使用,转换完成后,就不再关注之前的对象,且可以防止对之前对象的误调用。 使用方法代替有以下好处:
  • 不用再函数签名中重复书写self对应的类型
  • 代码的组织性和内聚性更强,对于代码维护和阅读而言,好处巨大
方法名要和结构体字段名相同

在Rust中,允许方法名和结构体的字段名相同

impl Rectangle{
	fn wifth(&self)->bool{
		self.width>0
	}
}
fn main(){
	let rect1 = Rectangle{
		width:30,
		height:50,
	};
	if rect1.width(){
		println!("The rectangle has a nonzero width: it is {}",rect1.width);
	}
}

当我们使用rect1.width()时,Rust知道我们调用的是它的方法,如果使用rect1.width(),则是访问它的字段 一般而言,方法和字段同名,往往适用于实现getter访问器,如:

mod my{
	pub struct Rectangle{
		width:u32,
		pub height:u32,
	}
	impl Rectangle{
		pub fn new(width:u32,height:u32)->Self{
		Rectangle{width,height}
		}
		pub fn width(&self)->u32{
			return self.width;
		}
		pub fn height(&self)->u32{
			return self.height;
		}
	}
}
fn main(){
	let rect1 = my::Rectangle::new(30,50);
	println!("{}",rect1.width());
	println!("{}",rect1.height());
	//println!("{}",rect1.width); //Error - the visibility of field defaults to private
	println!("{}",rect1.height);
}

当从外部模块访问结构体时,结构体的字段默认是私有的,目的是隐藏信息(封装);如果想要从模块外部获取Recatangle的字段,只需要将new``width``height方法设置为公开可见,那么用户就可以通过创建一个矩形,同时通过访问rect1.width()和rect1.height()来取矩形的高宽 因为width是私有的,当用户访问rect1.width字段时,就会error,注意在此例子中,Self指代的就是实现方法的结构体Rectangle 这种默认的可见性可以通过pub来进行覆盖,这样对于外部模块而言,就可以直接访问刚才用pub修饰的字段而无需经过访问器;这种可见性仅仅当从定义结构的模块外部访问时才重要,并且有隐藏信息的目的。

->运算符到哪里去了 C/C++中,2种运算符来调用方法:.直接在对象上调用方法,而->在一个对象的指针上调用方法,这时候需要先解引用指针; 简而言之,如果object是一个指针,那么object->something和(*object).something是一样的 Rust没有和->一样的运算符,但是Rust有自动引用和解引用的功能,方法调用时rust中少数几个拥有这种行为的地方 当使用object.something()调用方法时,rust会自动为object添加&,*以便使object与方法签名匹配;也就是说,这些代码时等价的

#[derive(Debug,Copy,Clone)]
struct Ponit{
	x:f64,
	y:f64,
}
impl Point{
	fn distance(&self,other:&Point)->f64{
		let x_squared = f64::powi(other.x-self.x,2);
		let y_squared = f64::powi(other.y-self.y,2);
		
		f64::sqrt(x_squared+y_squared);
	}
}
let p1 = Point{x:0.0,y:0.0};
let p2 = Point{x:5.0,y:6.5};
p1.distance(&p2);
(&p1).distance(&p2);

这种自动引用的行为之所以有效,是因为方法有一个明确的接收者——self的类型,在给出了接收者和方法名的前提下,rust可以明确的计算出仅仅读取(&self),做出修改(&mut self)或者时获取所有权(self). 事实上,rust对方法接收者的隐式借用让所有权在时间中更友好

带有多个参数的方法

方法和函数一样,可以带有多个参数:

impl Rectangle{
	fn area(&self)->u32{
		self.width*self.height
	}
	fn can_hold(&self,other:&Rectangle)->bool{
		self.width>other.width&&self.height>other.height
	}
}

fn main(){
	let rect1 = Rectangle{width:30,height:50};
	let rect2 = Rectangle{width:10,height:40};
	let rect3 = Rectangle{width:60,height:45};

	println!("Can rect1 hold rect2?{}",rect1.can_hold(&rect2));
	println!("Can rect1 hold rect3?{}",rect1.can_hold(&rect3));
}

关联函数

为结构体定义一个构造器方法,就是接收几个参数,然后构造并返回该结构体的实例->参数中不包含self.即可 这种定义在impl中且没有self的函数称之为关联函数,因为没有self,不能用f.read()的方式进行调用,因此是一个函数不是一个方法,但是又在impl当中,因此称之为关联函数 在之前的代码中,我们已经多次使用过关联函数,例如String::from,用于创建一个动态字符串

#[derive(Debug)]
struct Rectangle{
	width:u32,
	height:u32,
}
impl Rectangle{
	fn new(w:u32,h:u32)->Rectangle{
		Rectangle{width:w,height:h}
	}
}

rust中有一个约定俗成的规则,就是使用new作为构造器的名称,处于设计的考虑,rust特地没有使用new作为关键字 因为是函数,所以不能通过.来调用,需要用::来调用,例如let sq = Rectangle::new(3,3),这个方法位于结构体的命名空间中,::用于关联函数和模块创建的命名空间

多个impl定义

rust允许我们为一个结构体定义多个impl块,目的是提供更多灵活性和代码组织性,当语法多了之后,我们可以将相关的方法组织在同一个impl当中,那么就可以形成多个impl块

#[derive(Debug)]
struct Rectangle{
	width:u32,
	height:u32,
}
impl Rectangle{
	fn area(&self)->u32{
		self.width*self.height
	}
}
impl Rectangle{
	fn can_hold(&self,other:&Rectangle)->bool{
		self.width>other.width&&self.height>other.height
	}
}

为枚举实现方法

我们可以像结构体一样,为枚举实现方法

#![allow(unused)]
enum Message{
	Quit,
	Move{x:i32,y:i32},
	Write(String),
	ChangeColor(i32,i32,i32),
}
impl Message{
	fn call(&self){
		//Def
	}
}
fn main(){
	let m = Message::Write(String::from("hello"));
	//这里的::不要和关联函数的::混淆了
	m.call();
	
}

除了结构体和枚举,我们还能为trait实现方法