GitHub

核心概念

特质

Basics of Trait

在Rust中,特质(Traits)是一种定义方法签名的机制。

特质允许你定义一组方法的签名,但可以不提供具体的实现(也可以提供)。这些方法签名可以包括参数和返回类型,但不包括方法的实现代码。

任何类型都可以实现特质,只要它们提供了特质中定义的所有方法。这使得你可以为不同类型提供相同的行为。

特点:

  • 内置常量:特质可以内置常量(const),特质中定义的常量在程序的整个生命周期内是有效的
  • 默认实现:特质可以提供默认的方法实现。如果类型没有为特质中的某个方法提供自定义实现,将会使用默认实现。
  • 多重实现:类型可以实现多个特质,这允许你将不同的行为组合在一起。
  • 特质边界:在泛型代码中,你可以使用特质作为类型约束。这被称为特质边界,它限制了泛型类型必须实现的特质。
  • Trait Alias: Rust还支持trait alias,这允许你为复杂的trait组合创建简洁的别名,以便在代码中更轻松地引用。

例子:

trait Greeter {
fn greet(&self);
fn hello() {
println!("hello");
}
}
struct Person {
name: String,
}
impl Greeter for Person {
fn greet(&self) {
println!("greet {}", self.name);
}
}
fn main() {
let person = Person {
name: "Yz".to_owned(),
};
person.greet(); // greet Yz
Person::hello(); // hello
}

Trait Object & Box

Trait Object

  1. 在运行时动态分配的对象
    • “运行时泛型”
    • 比泛型要灵活的多
  2. 可以在集合中混入不同类型对象
    • 更容易处理相似的数据
  3. 有一些小小的性能损耗

Keyword dyn

dyn是Rust中的关键字,用于声明特质对象(trait object)的类型。特质对象是实现特定特质(trait)的类型的实例,但其具体类型在编译时是未知的。因此,为了让编译器知道我们正在处理的是特质对象,我们需要在特质名称前面加上dyn关键字。

dyn关键字的作用是指示编译器处理特质对象。

Three Forms of Data Transfer in Rust

  • 不可变引用
    • &dyn Trait
  • 可变引用
    • &mut dyn Trait
  • Move语义所有权转移
    • 特质需要用Box<dyn Trait>实现Move,如果你需要在函数调用之间传递特质的所有权,并且希望避免在栈上分配的大量内存,可以使用Box<dyn Trait>

Three Ways to define trait Object

  • 第一种:

    let o = Object{};
    let o_obj: &dyn Object = &o;
  • 第二种:

    let o_obj: &dyn Object = &Object{}
  • 第三种:

    let o_obj: Box<dyn Object> = Box::new(Object{});

第一种和第二种都是创建不可变引用,第三种最常用也是最灵活,一般来说会使用Box和特质来组成集合元素。

例子:

1struct Obj {}
2
3trait Overview {
4 fn overview(&self) -> String;
5}
6
7impl Overview for Obj {
8 fn overview(&self) -> String {
9 String::from("Obj")
10 }
11}
12
13// 不可变引用
14fn call_obj(item: &impl Overview) {
15 println!("Overview {}", item.overview());
16}
17
18// Move
19fn call_obj_box(item: Box<dyn Overview>) {
20 println!("Overview box {}", item.overview());
21}
22
23trait Sale {
24 fn amount(&self) -> f64;
25}
26
27struct Common(f64);
28
29impl Sale for Common {
30 fn amount(&self) -> f64 {
31 self.0
32 }
33}
34
35struct TenDiscount(f64);
36
37impl Sale for TenDiscount {
38 fn amount(&self) -> f64 {
39 self.0 - 10.0
40 }
41}
42
43struct TenPercentDiscount(f64);
44
45impl Sale for TenPercentDiscount {
46 fn amount(&self) -> f64 {
47 self.0 * 0.9
48 }
49}
50
51
52fn calculate(sales: &Vec<Box<dyn Sale>>) -> f64 {
53 sales.iter().map(|sale|sale.amount()).sum()
54}
55
56fn main() {
57 let a = Obj{};
58 call_obj(&a); // Overview Obj
59 println!("{}", a.overview()); // Obj
60 let b_a = Box::new(Obj{});
61 call_obj_box(b_a); // Overview box Obj
62 // println!("{}", b_a.overview())
63
64 let c = Box::new(Common(100.0));
65 let t = Box::new(TenDiscount(100.0));
66 let t2 = Box::new(TenPercentDiscount(100.0));
67
68 let sales: Vec<Box<dyn Sale>> = vec![c, t, t2];
69 println!("pay {}", calculate(&sales)) // pay 280
70}

Trait Object & Generics

Difference Ways of Writing Generics & impl

  • fn call(item1: &impl Trait, item2: &impl Trait);

    可以是不同类型

  • fn call_generic<T: Trait>(item1: &T, item2: &T);

    可以是相同类型

Multiple Trait Bounds

  • fn call(item1: &(impl Trait + AnotherTrait));
  • fn call_generic<T: Trait + AnotherTrait>(item1: &T);

例子:

1trait Overview {
2 fn overview(&self) -> String {
3 String::from("Course")
4 }
5}
6
7trait Another {
8 fn hell(&self) {
9 println!("welcome to hell")
10 }
11}
12
13struct Course {
14 headline: String,
15 author: String,
16}
17
18// Course实现两种特质
19impl Overview for Course {}
20impl Another for Course {}
21
22struct AnotherCourse {
23 headline: String,
24 author: String,
25}
26
27impl Overview for AnotherCourse {}
28
29fn call_overview(item: &impl Overview) {
30 println!("Overview {}", item.overview());
31}
32
33fn call_overview_generic<T: Overview>(item: &T) {
34 println!("Overview generic {}", item.overview());
35}
36
37fn call_overviewT(item: &impl Overview, item1: &impl Overview) {
38 println!("OverviewT {}", item.overview());
39 println!("OverviewT {}", item1.overview());
40}
41
42// 只允许传入同类型结构体的引用
43fn call_overviewTT<T: Overview>(item: &T, item1: &T) {
44 println!("OverviewTT {}", item.overview());
45 println!("OverviewTT {}", item1.overview());
46}
47
48// 多绑定
49fn call_mul_bind(item: &(impl Overview + Another)) {
50 println!("Overview {}", item.overview());
51 item.hell();
52}
53
54// 多绑定泛型
55fn call_mul_bind_generic<T>(item: &T)
56where
57 T: Overview + Another,
58{
59 println!("Overview {}", item.overview());
60 item.hell();
61}
62
63fn main() {
64 let c0 = Course {
65 headline: "ff".to_owned(),
66 author: "yy".to_owned(),
67 };
68 let c1 = Course {
69 headline: "ff".to_owned(),
70 author: "yy".to_owned(),
71 };
72
73 let c2 = AnotherCourse {
74 headline: "ff".to_owned(),
75 author: "yz".to_owned(),
76 };
77 call_overview(&c1); // Overview Course
78 call_overview_generic(&c1); // Overview generic Course
79 call_overviewT(&c1, &c2);
80 // OverviewT Course
81 // OverviewT Course
82 call_overviewTT(&c0, &c1); // 应用范围宅一些
83 // OverviewTT Course
84 // OverviewTT Course
85 call_overviewT(&c0, &c1);
86 // OverviewT Course
87 // OverviewT Course
88 call_mul_bind(&c1);
89 // Overview Course
90 // welcome to hell
91 call_mul_bind_generic(&c1);
92 // Overview Course
93 // welcome to hell
94}

Overloading(Operator

  • 只需要实现相应的特质

为结构体实现一个加减的例子:

1use std::ops::Add;
2use std::ops::Sub;
3// 编译时
4#[derive(Debug)]
5struct Point<T> {
6 x: T,
7 y: T,
8}
9
10// T的这样类型,它可以执行相加的操作
11impl<T> Add for Point <T>
12 where T: Add<Output = T>
13{
14 type Output = Self;
15 fn add(self, rhs: Self) -> Self::Output {
16 Point {
17 x: self.x + rhs.x,
18 y: self.y + rhs.y,
19 }
20 }
21
22}
23
24// 相减
25impl <T> Sub for Point <T>
26 where T: Sub<Output = T>
27{
28 type Output = Self;
29 fn sub(self, rhs: Self) -> Self::Output {
30 Point {
31 x: self.x - rhs.x,
32 y: self.y - rhs.y,
33 }
34 }
35}
36fn main() {
37 let i1 = Point{x: 1, y: 2};
38 let i2 = Point{x: 1, y: 3};
39 let sum = i1 + i2;
40 println!("{:?}", sum); // Point { x: 2, y: 5 }
41
42 let i1 = Point{x: 1, y: 2};
43 let i2 = Point{x: 1, y: 3};
44 let sub = i1 - i2;
45 println!("{:?}", sub); // Point { x: 0, y: -1 }
46}

Trait & Polymorphism and inheritance

Rust并不支持传统的继承的概念,但是你可以在特质中通过层级化来完成你的需求。

Rust选择了一种函数式的编程范式,即“组合和委托”而非“继承”。

编程语言的大势也是组合优于继承。

例子:

1use std::collections::VecDeque;
2
3// 多态
4trait Driver {
5 fn drive(&self);
6}
7
8struct Car;
9
10impl Driver for Car {
11 fn drive(&self) {
12 println!("Car is driving")
13 }
14}
15struct SUV;
16
17impl Driver for SUV {
18 fn drive(&self) {
19 println!("SUV is driving")
20 }
21}
22
23fn road(vehicle: &dyn Driver) {
24 vehicle.drive()
25}
26
27// 继承思想
28// 单向特质
29trait Queue {
30 fn len(&self) -> usize;
31 fn push_back(&mut self, n: i32);
32 fn pop_front(&mut self) -> Option<i32>;
33}
34
35// 双向特质
36trait Deque: Queue {
37 fn push_front(&mut self, n: i32);
38 fn pop_back(&mut self) -> Option<i32>;
39}
40
41#[derive(Debug)]
42struct List {
43 data: VecDeque<i32>,
44}
45
46impl List {
47 fn new() -> Self {
48 let data = VecDeque::<i32>::new();
49 Self{data}
50 }
51}
52
53impl Deque for List {
54 fn push_front(&mut self, n: i32) {
55 self.data.push_front(n)
56 }
57
58 fn pop_back(&mut self) -> Option<i32> {
59 self.data.pop_back()
60 }
61}
62
63impl Queue for List {
64 fn len(&self) -> usize {
65 self.data.len()
66 }
67
68 fn pop_front(&mut self) -> Option<i32> {
69 self.data.pop_back()
70 }
71
72 fn push_back(&mut self, n: i32) {
73 self.data.push_back(n)
74 }
75}
76
77fn main() {
78 road(&Car); // Car is driving
79 road(&SUV); // SUV is driving
80
81 let mut l = List::new();
82 l.push_back(1);
83 l.push_front(0);
84 println!("{:?}", l); // List { data: [0, 1] }
85 l.push_front(2);
86 println!("{:?}", l); // List {data: [2, 0, 1] }
87 l.push_back(3);
88 println!("{:?}", l); // List {data: [2, 0, 1, 3]}
89 println!("{}", l.pop_back().unwrap()); // 3
90 println!("{:?}", l); // List {data: [2, 0, 1] }
91}

Common Type of Trait

  • Debug
  • Clone
  • PartialEq

例如:

1// Clone Copy Debug PartialEq
2// 层级 打个比方,结构体里面有个枚举,那么结构体实现Clone,里面的枚举也实现Clone
3
4// 给枚举Race实现Debug,Clone
5#[derive(Debug, Clone, Copy)]
6enum Race {
7 White,
8 Yellow,
9 Black,
10}
11
12impl PartialEq for Race {
13 fn eq(&self, other: &Self) -> bool {
14 match(self, other) {
15 (Race::White, Race::White) => true,
16 (Race::Yellow, Race::Yellow) => true,
17 (Race::Black, Race::Black) => true,
18 _ => false,
19 }
20 }
21}
22
23// 给结构体User实现Debug, Clone
24#[derive(Debug, Clone)]
25struct User {
26 id: u32,
27 name: String,
28 race: Race,
29}
30
31impl PartialEq for User {
32 fn eq(&self, other: &Self) -> bool {
33 self.id == other.id && self.name == other.name && self.race == other.race
34 }
35}
36
37fn main() {
38 let user = User{
39 id: 3,
40 name: "John".to_owned(),
41 race: Race::Yellow,
42 };
43
44 println!("{:#?}", user);
45 // User {
46 // id: 3,
47 // name: "John",
48 // race: Yellow,
49 // }
50 let user2 = user.clone();
51 println!("{:#?}", user2);
52 // User {
53 // id: 3,
54 // name: "John",
55 // race: Yellow,
56 // }
57 println!("{:#?}", user == user2); // true
58}
上一篇
泛型