impl

此章(Rust面向对象的所有篇目)假设你已经接触学习过面向对象(C++最好)。

如果你正在别的语言中学习面向对象思想,建议你先从中学习后再看此章节下的所有篇章

使用impl(implementations:实现)将函数关联到同作用域内的结构体和枚举上


面向对象有一个重要的思想:将方法(函数)绑定(关联)到类(结构体)上。

impl就是用于将函数绑定到结构体的Rust关键字,下面是一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 客户信息
struct Info {
id: u32,
age: u8,
}

impl Info {
fn pln(info: Info) {
println!("id: {}\nage: {}", info.id, info.age);
}

fn c_id(male: bool) -> u32 {
male as u32
}
}

// 一个struct可以有多个impl块
impl Info {
fn test() {}
}

fn main() {
let info = Info{ id: 0, age: 18 };
// 使用Info的pln函数
Info::pln(info);
}

运行结果:

1
2
id: 0
age: 18

impl也可以用于枚举上:

1
2
3
4
5
6
7
8
9
10
11
12
13
enum Num {
One,
Two,
}

impl Num {
fn convert(num: Num) -> i32 {
match num {
Num::One => 1,
Num::Two => 2,
}
}
}

impl中定义的函数被称为关联函数,也被称为方法。

另外提醒一点,impl块内也不能声明同名函数。

Selfself


Self关键字表示所impl结构体的类型,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
struct Test1;

impl Test1 {
// 这里Self等价于Test1
fn test(t1: Self) {}
}

struct Test2;

impl Test2 {
// 这里Self等价于Test2
fn test(t1: Self) {}
}

self关键字用于表明该函数是实例函数,使用了self作为参数名的关联函数就能通过实例调用的方式调用,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
struct Test {
ck: i32,
}

impl Test {
// 需要注意的是,self必须是第一个参数
fn get_ck(self: Self) -> i32 {
// self就是Self的一个实例,因此可以获取ck字段
self.ck
}

// 为了方便,编译器支持这种简写
// 单个self参数等价于self: Self
fn get_tk(self, a: bool) -> i32 {
0
}
}

fn main() {
let t = Test{ ck: 12 };
// 通过实例调用的方式调用get_ck()
// t.get_ck()等价于Test::get_ck(t)
println!("{}", t.get_ck());
}

看到self,有人可能会想到C++的this指针。但是与C++不同的是,要想使用self,必须在函数参数首部添加self参数。

回顾一下,函数及其返回值中提到:

1
2
// 注意参数二前面加了`mut`,此时便可获取arg_name2的修 w改权
fn function_name(/* arg_name1: type, mut arg_name2: type */) -> return_type {

self传参时,参数是不可变的。类似的,可以在self前加上mut以获取self的修改权。

1
2
3
4
5
impl Test {
fn test(mut self) {
self.ck = 1;
}
}

让我们尝试使用test()

1
2
3
4
5
fn main() {
let t = Test{ ck: 1 };
t.test();
println!("{}", t.ck);
}

cargo run,哦豁,编译报错:

1
2
3
4
13 |     t.test();
| ------ `t` moved due to this method call
14 | println!("{}", t.ck);
| ^^^^ value borrowed here after move

好了,看到这里你也许以为我会说明原因,但并不是,因为原因脱离了本篇的主题。之所以提供上述错误示例,是想提醒一下不要因为遇到上述问题而困惑并停滞学习,我将会在所有权系统一篇解释相关原因,让我们跳过这个问题,继续学习。