[易学易懂系列|rustlang语言|零基础|快速入门|(14)|Impls & Traits实现与特征]

释放双眼,带上耳机,听听看~!

[易学易懂系列|rustlang语言|零基础|快速入门|(14)]

有意思的基础知识

Impls & Traits实现与特征

我之前说到的struct结构体,其实就类似于面向对象语言中的类class。

但这个struct,并没有定义方法或函数。

那要怎么办呢?

Rust用关键词impls(实现)来定义struct和enum的方法或函数。

而trait(特征),类似于面向对象语言中的接口interface。

特征,是用来定义要实现的方法,一个类型可以有多个特征。特征可以有默认实现函数,这个默认函数可以在运行时重写。

我们来看看代码:

1.没有trait特征的impl实现:


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
27
1struct Player {
2    first_name: String,
3    last_name: String,
4}
5
6impl Player {
7    
8    fn full_name(&self) -> String {
9        format!("{} {}", self.first_name, self.last_name)
10    }
11}
12
13fn main() {
14    let player_1 = Player {
15        first_name: "Rafael".to_string(),
16        last_name: "Nadal".to_string(),
17    };
18
19    println!("Player 01: {}", player_1.full_name());
20}
21
22// ⭐️ Implementation must appear in the same crate as the self type
23
24// ? And also in Rust, new traits can be implemented for existing types even for types like i8, f64 and etc.
25// Same way existing traits can be implemented for new types you are creating.
26// But we can not implement existing traits into existing types.
27

我们来看看上面的代码,其中这段代码,跟之前的函数,有点不一样:


1
2
3
4
5
6
1impl Player {
2    fn full_name(&self) -> String {
3        format!("{} {}", self.first_name, self.last_name)
4    }
5}
6

这里第一个参数:&self,它代表什么意思呢?

&self代表结构体Player的实例。

当然这里的第一个参数,可以是:self, &self, 或&mut self

self是一个栈内值 。

&self是一个引用值(不可变),数据在堆heap里分配空间。

&mut self是一个可变的引用值,数据在堆heap里分配空间

所以通过,self.first_name, self.last_name,我们就可以访问结构体的属性first_name,last_name。

这里的impl 代码块代表结构体Player的具体实现方法,impl后面的类型,必须是Player。

1.特征(trait)的实现方式:


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
1struct Player {
2    first_name: String,
3    last_name: String,
4}
5
6trait FullName {
7    fn full_name(&self) -> String;
8}
9
10impl FullName for Player {
11    fn full_name(&self) -> String {
12        format!("{} {}", self.first_name, self.last_name)
13    }
14}
15
16fn main() {
17    let player_2 = Player {
18        first_name: "Roger".to_string(),
19        last_name: "Federer".to_string(),
20    };
21
22    println!("Player 02: {}", player_2.full_name());
23}
24
25// ? Other than functions, traits can contain constants and types.
26

我们可以看到上面代码中加了一个特征的定义,代码可读性好很多:


1
2
3
4
1trait FullName {
2    fn full_name(&self) -> String;
3}
4

好,现在我们加上默认函数(default method):


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
1struct Player {
2    first_name: String,
3    last_name: String,
4}
5trait FullName {
6    fn full_name(&self) -> String;
7    fn baz(&self) { println!("We called baz.");// 默认函数
8}
9impl FullName for Player {
10    fn full_name(&self) -> String {
11        format!("{} {}", self.first_name, self.last_name)
12    }
13}
14
15fn main() {
16    let player_2 = Player {
17        first_name: "Roger".to_string(),
18        last_name: "Federer".to_string(),
19    };
20
21    println!("Player 02: {}", player_2.full_name());
22    player_2.baz();//直接调用默认函数
23}
24
25

3.关联函数(Associated functions):

请看如下例子:


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
27
28
1struct Player {
2    first_name: String,
3    last_name: String,
4}
5
6impl Player {
7    fn new(first_name: String, last_name: String) -> Player {
8        Player {
9            first_name : first_name,
10            last_name : last_name,
11        }
12    }
13
14    fn full_name(&self) -> String {
15        format!("{} {}", self.first_name, self.last_name)
16    }
17}
18
19fn main() {
20    let player_name = Player::new("Serena".to_string(), "Williams".to_string()).full_name();
21    println!("Player: {}", player_name);
22}
23
24// We have used :: notation for `new()` and . notation for `full_name()`
25
26// ? Also in here, instead of using new() and full_name() separately as two expressions,
27// we can use Method Chaining. ex. `player.add_points(2).get_point_count();`
28

上面代码,我们看到多了一个函数new(注意这个函数的参数,不用self关键字),我们看到这个new函数跟java中的构造函数很像。

这个函数可以不通过创建对象实例,就可以直接调用,一般用双冒号::,如:Player::new。

在Rust,我们叫这种函数为:关联函数(Associated functions)。

我们再来看看特征的其他用法:

带泛型的特征:


1
2
3
4
5
6
7
8
9
10
11
12
1trait From<T> {
2    fn from(T) -> Self;
3}
4    impl From<u8> for u16 {
5        //...
6    }
7    impl From<u8> for u32{
8        //...
9    }
10
11// Should specify after the trait name like generic functions
12

带继承关系的特征:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1trait Person {
2    fn full_name(&self) -> String;
3}
4trait Expat{
5    fn tax(&self)->f64;
6}
7
8    trait Employee : Person { // Employee inherits from person trait
9      fn job_title(&self) -> String;
10    }
11
12    trait ExpatEmployee : Employee + Expat { // ExpatEmployee inherits from Employee and Expat traits
13      fn additional_tax(&self) -> f64;
14    }
15

特征对象:


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
27
28
29
30
31
32
33
34
35
1trait GetSound {
2    fn get_sound(&self) -> String;
3}
4
5struct Cat {
6    sound: String,
7}
8    impl GetSound for Cat {
9        fn get_sound(&self) -> String {
10            self.sound.clone()
11        }
12    }
13
14struct Bell {
15    sound: String,
16}
17    impl GetSound for Bell {
18        fn get_sound(&self) -> String {
19            self.sound.clone()
20        }
21    }
22
23
24fn make_sound<T: GetSound>(t: &T) {
25    println!("{}!", t.get_sound())
26}
27
28fn main() {
29    let kitty = Cat { sound: "Meow".to_string() };
30    let the_bell = Bell { sound: "Ding Dong".to_string() };
31
32    make_sound(&kitty); // Meow!
33    make_sound(&the_bell); // Ding Dong!
34}
35

上面的代码我们可以看到,Rust实现多态,可以用动态分发的机制,所谓动态分发,就是在运行时,才决定真正的实现,并加载这个实现。

以上,希望对你有用。


1
2
1如果遇到什么问题,欢迎加入:rust新手群,在这里我可以提供一些简单的帮助,加微信:360369487,注明:博客园+rust
2

参考:https://doc.rust-lang.org/rust-by-example/generics/impl.html

https://learning-rust.github.io/docs/b5.impls_and_traits.html

https://doc.rust-lang.org/book/ch05-03-method-syntax.html

给TA打赏
共{{data.count}}人
人已打赏
安全技术

C++调用Python

2022-1-11 12:36:11

病毒疫情

千里驰援书写时代篇章

2020-3-23 8:32:00

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索