定义方法, 带有更多参数的方法,关联函数,多个impl块
方法(method)
与函数类似:它们使用 fn
关键字和名称声明,可以拥有参数和返回值,同时包含在某处调用该方法时会执行的代码。Rust
通过impl
关键字在struct
、enum
或者trait
对象上实现方法调用语法(method call syntax)
,并且它们第一个参数总是 self
,它代表调用该方法的结构体实例。
先看一个例子,如何用方法实现计算长方形的面积:
struct Rectangle {
width: u32,
height: u32,
}
// 在 Rectangle 结构体上定义 area 方法
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
println!("长方形的面积为: {} ", rect1.area());
}
为了使函数定义于Rectangle
的上下文中,我们开始了一个 impl
块(impl 是 implementation 的缩写)
,这个 impl
块中的所有内容都将与 Rectangle
类型相关联。在 Rectangle
实例上调用 area
方法。方法语法获取一个实例并加上一个点号,后跟方法名、圆括号以及任何参数。
在 area
的签名中,使用&self
来替代 rectangle: &Rectangle
,&self
实际上是self: &Self
的缩写。在一个 impl
块中,Self
类型是 impl
块的类型的别名。方法的第一个参数必须有一个名为 self
的Self
类型的参数,所以 Rust
让你在第一个参数位置上只用 self
这个名字来缩写。
这里选择 &self
的理由跟在函数版本中使用 &Rectangle
是相同的:我们并不想获取所有权,只希望能够读取结构体中的数据,而不是写入。如果想要在方法中改变调用方法的实例,需要将第一个参数改为 &mut self
。通过仅仅使用 self
作为第一个参数来使方法获取实例的所有权是很少见的;这种技术通常用在当方法将 self
转换成别的实例的时候,这时我们想要防止调用者在转换之后使用原始的实例。
使用方法替代函数,除了可使用方法语法和不需要在每个函数签名中重复 self
的类型之外,其主要好处在于组织性。我们将某个类型实例能做的所有事情都一起放入 impl
块中,而不是让将来的用户在我们的库中到处寻找 Rectangle
的功能。
请注意,我们可以选择将方法的名称与结构中的一个字段相同。例如,我们可以在 Rectangle
上定义一个方法,并命名为 width
:
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn width(&self) -> bool {
self.width > 0
}
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
if rect1.width() {
println!("width大于0的值为:{}", rect1.width)
}
}
在这里,我们选择让 width
方法在实例的 width
字段的值大于 0
时返回 true
,等于 0
时则返回 false
,我们可以在同名的方法中使用同名的字段。在 main
中,当我们在 rect1.width
后面加上括号时。Rust
知道我们指的是方法 width
。当我们不使用圆括号时,Rust
知道我们指的是字段 width
。
我们让一个 Rectangle
的实例获取另一个 Rectangle
实例,如果 self
(第一个 Rectangle
)长方形面积大于二个长方形面积则返回 true
;否则返回 false
:
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
fn compare(&self, other: &Rectangle) -> bool {
self.area() > other.area()
}
}
fn main() {
let rect1 = Rectangle {
width: 40,
height: 50,
};
let rect2 = Rectangle {
width: 30,
height: 50,
};
let rect3 = Rectangle {
width: 50,
height: 60,
};
println!(
"第一个长方形面积({})>第二个长方形面积({})={}",
rect1.area(),
rect2.area(),
rect1.compare(&rect2)
);
println!(
"第一个长方形面积({})>第三个长方形面积({})={}",
rect1.area(),
rect3.area(),
rect1.compare(&rect3)
);
}
输出:
$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.03s
Running `target/debug/variables`
第一个长方形面积(2000)>第二个长方形面积(1500)=true
第一个长方形面积(2000)>第三个长方形面积(3000)=false
可以在 self
后增加多个参数,而且这些参数就像函数中的参数一样使用。
所有在 impl
块中定义的函数被称为 关联函数(associated functions)
,因为它们与 impl
后面命名的类型相关。我们可以定义不以 self
为第一参数的关联函数(因此不是方法),因为它们并不作用于一个结构体的实例。我们已经使用了一个这样的函数:在 String
类型上定义的 String::from
函数。
不是方法的关联函数经常被用作返回一个结构体新实例的构造函数。这些函数的名称通常为 new
,但 new
并不是一个关键字 ,我们使用 new
开头第一个例子:
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn new(width: u32, height: u32) -> Self {
Rectangle { width, height }
}
fn area(&self) -> u32 {
self.width * self.height
}
}
fn main() {
// 方法一 使用关联函数再调用方法
let rect1 = Rectangle::new(30, 50);
println!("方法一长方形的面积为: {} ", rect1.area());
// 方法二 使用关联函数和方法链接
println!("方法二长方形的面积为: {} ", Rectangle::new(30, 50).area());
}
关键字 Self
在函数的返回类型中代指在 impl
关键字后出现的类型,在这里是 Rectangle
每个结构体都允许拥有多个 impl
块:
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
impl Rectangle {
fn compare(&self, other: &Rectangle) -> bool {
self.area() > other.area()
}
}
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!