方法和函数类似,但是方法必须归属到某个结构体、枚举或者trait对象内,而函数则不必。方法的一个参数总是self,他表示被调用的这个结构的方法本身的实例。
-
-
- 定义方法的语法
-
结构体方法定义的函数的语法如下所示:
rust
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
fn main() {
let rect = Rectangle {
width: 30,
height: 50,
};
println!("长方形的面积是:{}", rect.area());
}
定义结构体的函数,必须在结构体前面以impl这个关键字开始,后面跟着结构体的名称,在然后使用花括号包含需要实现的功能的代码。在主函数中调用rect的area方法。rect是结构体Rectangle结构体的实例,使用小数点"."分割,后面跟着方法名、小括号和一些参数。参数"&self"是self: &Self的简写形式,它表示的是rectangle:&Rectangle的替代品。它是impl要实现的结构体本身。符号"&"表示借用所用权,因此它所指的变量是不可变的。在这个方法使用借用,表示它不会取得所有权,只需要读取机构体中的属性值。如果需要修改属性值,则需要使用可变借用使用符号"&mut self"。一般很少使用self直接获取所有权,但是如果需要将self转换为其他某些东西,且需要在转换后组织调用者访问原有的实例,一般可以直接用self。
使用方法而不是函数的主要作用是将相关功能组合在一起,而不是在不同的库中进行查找。
注意,我们可以使用和属性名称相同的方法名称,例如下面使用相同的width作为方法名。
rust
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn width(&self) -> bool {
self.width > 0
}
}
fn main() {
let rect = Rectangle {
width: 30,
height: 50,
};
println!("长方形的宽度是否大于零:{}", rect.width());
}
width后面跟着小括号则表示是方法,如果后面没有小括号则表示为属性。当时当使用相同名称的方法时,通常会返回该属性值,这样的方法通常叫做getter,Rust不会像其他语言一样会自动实现这样的功能。
-
-
- 操作符"->"到哪去了?
-
在C或C++语言中,有两种不同的操作符可以调用的它的方法,如果object是一个指针,则调用方法的语法为:object->something(),或者使用(*object).something()。
Rust语言没有"->"操作符;但是它有一个称之为自动引用和解引用的特性。调用方法是使用该特性为数不多的几种场景之一。这个特性会自动将符号"&"、"&mut"或"*"到object之前,以便它符合方法的签名,换而言之,下面的两行代码是相同的。
p1.distance(&p2);
(&p1).distance(&p2);
第一条看上去更加清晰,因为方法有一个更加清晰的接收者,self类型,因此自动引用行为就起作用了。指定了接收者方法名,Rust就可以明确到底是使用借用、可变借用还是所有权转移。这大大符合了人类的使用习惯。
-
-
- 有更多参数的方法
-
下面我们增加一个方法can_hold,它有第二参数,它是Rectangle的借用类型,它和自己的宽和高进行比较,如果都是大于,则返回ture,否则返回false。代码如下所示:
rust
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
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: 36,
height: 50,
};
let rect3 = Rectangle {
width: 10,
height: 20,
};
println!("rect1是否大于rect2:{}", rect1.can_hold(&rect2));
println!("rect1是否大于rect3:{}", rect1.can_hold(&rect3));
}
程序运行结果为:
rust
rect1是否大于rect2:false
rect1是否大于rect3:true
-
-
- 关联函数
-
所有在impl块中定义的函数都叫做关联函数,因为它和结构体本身是有相关性的。关联函数的参数不会将slef作为第一个参数,因为它不需要使用到结构体本身。例如:定义在Sting类型中的String::from函数。
关联函数不是方法,它通常用作构造器,返回结构体本身的一个实例。通常这些构造器叫做new,new并不是一个特殊的名字,并没有内置于语言中。
下面的示例中有个关联函数square,它会创建宽和高相同的一个正方形,因为宽和高相同,只需要一个参数就可以了。
rust
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn square(length: u32)->Self{
Self {
width: length,
height: length,
}
}
}
fn main() {
let rect1 = Rectangle::square(10);
println!("rect1{:#?}", rect1);
}
-
-
- 多个impl块
-
结构体运行有多个impl块,它是一种有效的语法,可能是为了不修改之前版本的代码,而使用功能的迭代。
rust
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn square(length: u32) -> Self {
Self {
width: length,
height: length,
}
}
}
impl Rectangle {
fn area(&self)->u32 {
self.width * self.height
}
}
fn main() {
let rect1 = Rectangle::square(10);
println!("rect1{:#?}", rect1);
println!("正方体的面积是:{:?}", rect1.area());
}
结构体可以创建你自己的类型,这对于你的工作领域是特别有意义的。通过使用结构体,你可以将相互关联的数据相互组合在一起,使你的代码更加清晰。在impl块中,你可以定义和你类型有关的函数、方法(结构体实例本身应该具备的一些行为)。