这篇博客来聊一下 结构体的方法,本质上就是为某个结构体而定义的方法 (函数)。拿面向对象语言来说,写了一个类,还可能会在这个类里写一些方法,基本上同样的套路。或者说,定义的这些方法,是与这个结构体有关的。

不太严谨的解释,这里所谓的方法,其实就是一个函数。

结构体方法的定义

看下面的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 这里定义一个矩形的结构体
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}

// 这里的 impl xxx 是固定的,xxx 就是你要为哪一个结构体去定义一些方法
impl Rectangle {
// 这是一个计算面积的方法
fn area(&self) -> u32 {
self.width * self.height
}

// 这是一个计算周长的方法
fn perimeter(&self) -> u32 {
self.width * 2 + self.height * 2
}

// 这是一个计算能否包含另一个矩形的方法
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}

!!! 注意看上面的代码及 注释

结构体的方法 第一个参数总是 &self 表示定义好的结构体 实例 自身

下面是使用这个结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fn main() {
let r1 = Rectangle {width: 128, height: 90};
let r2 = Rectangle {width: 50, height: 30};

println!("{:#?}", r1);
println!("{:#?}", r2);

// 调用结构体的 area() 方法
println!("r area: {}", r1.area());

// 调用结构体的 perimeter() 方法
println!("r perimeter: {}", r1.perimeter());

// 调用结构体的 can_hold() 方法
println!("r1 can hold r2: {}", r1.can_hold(&r2));
}

注意上面的代码,我们打印结构体实例的时候,以前是用 {:?},而这次用的是 {:#?}。这是因为,使用 {:#?} 能以更美观的方式打印出这个结构体的数据来

如果要定义可以修改的结构体,只要加上 mut 就行,看下面的代码

1
2
3
4
5
6
7
8
fn main() {
let mut r1 = Rectangle {width: 100, height: 50};
println!("{:#?}", r1);

// 修改了 r1 的 width
r1.width = 1000;
println!("{:#?}", r1);
}

关联函数

impl 代码块中,定义一些不以 &self 作为参数的函数,这些函数被称为 关联函数。就是说,这些函数,不针对某一个具体的结构体实例,而是针对这个结构体本身。

直接看下面的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}

impl Rectangle {
// 创建一个 矩形 实例
fn new(width: u32, height: u32) -> Rectangle {
Rectangle {
width,
height,
}
}

// 创建一个 正方形 实例
fn new_square(size: u32) -> Rectangle {
Rectangle {
width: size,
height: size,
}
}
}

下面代码是 使用 关联函数

1
2
3
4
5
6
fn main() {
let r1 = Rectangle::new(100, 80);
let r2 = Rectangle::new_square(100);
println!("{:#?}", r1);
println!("{:#?}", r2);
}

对于一个结构体方法的调用,我们使用 r1.area() 这样的形式。而对于 关联函数 的调用,需要使用 :: 这个操作符。