封装(encapsulation)

rust 的封装是基于结构体,而不是对象,结构体就是 rust 的对象,这个和go一样。
默认情况下,结构体只有字段。
注意,结构体自身被标记为 pub,这样其他代码就可以使用这个结构体,但是在结构体内部的字段仍然是私有的!!!!
可以通过实现结构体,来为结构体添加方法,也是和go一样的特性!!

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
36
37
38
39
40
41
42
pub struct AveragedCollection {
list: Vec<i32>,
average: f64,
}

impl AveragedCollection {
// 这个 self 是不是有熟悉的感觉,python 中大量使用 self
pub fn add(&mut self, value: i32) {
self.list.push(value);
self.update_average();
}

pub fn remove(&mut self) -> Option<i32> {
let result = self.list.pop();
match result {
Some(value) => {
self.update_average();
Some(value)
}
None => None,
}
}

pub fn average(&self) -> f64 {
self.average
}

fn update_average(&mut self) {
let total: i32 = self.list.iter().sum();
self.average = total as f64 / self.list.len() as f64;
}
}

fn main() {
let mut average = AveragedCollection {
list: vec![1, 2, 3],
average: 3.0,
};
// 注意看,这个里用 . 而不是 ::,在rust中对象使用是. 进行调用
average.add(3);
}

继承(Inheritance)--rust 没有继续机制!!

是一个很多编程语言都提供的机制,一个对象可以定义为继承另一个对象定义中的元素,这使其可以获得父对象的数据和行为,而无需重新定义。
如果一个语言必须有继承才能被称为面向对象语言的话,那么 Rust 就不是面向对象的。因为没有宏则无法定义一个结构体继承父结构体的成员和方法。
其实 go 也是这么干的,这两哥们都不愿意使用继承,而都是通过组合的形式。

rust 如何实现代码复用?
Rust 代码中可以使用默认 trait 方法实现来进行有限的共享。

多态(Polymorphism)

Rust 则通过泛型来对不同的可能类型进行抽象,并通过 trait bounds 对这些类型所必须提供的内容施加约束。
这有时被称为 bounded parametric polymorphism。
Rust 选择了一个不同的途径,使用 trait 对象而不是继承。
其实 trait 就是类似于其它语言的接口(Interface),然后以这种形式实现多态。

举个例子:

定义一下Shape的Trait,其中包含一个area方法。
然后,分别为CircleRectangle结构体实现了Shape Trait。
最后,编写了一个print_area函数,它接受一个实现了Shape Trait的参数,并打印出其面积。

通过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
27
28
29
30
31
32
33
34
35
36
trait Shape {
fn area(&self) -> f64;
}

struct Circle {
radius: f64,
}

impl Shape for Circle {
fn area(&self) -> f64 {
std::f64::consts::PI * self.radius * self.radius
}
}

struct Rectangle {
width: f64,
height: f64,
}

impl Shape for Rectangle {
fn area(&self) -> f64 {
self.width * self.height
}
}

fn print_area(shape: &dyn Shape) {
println!("Area: {}", shape.area());
}

fn main() {
let circle = Circle { radius: 1.0 };
let rectangle = Rectangle { width: 2.0, height: 3.0 };

print_area(&circle);
print_area(&rectangle);
}

这个其实和go实现多态的方式是一样的,也是基于实现接口的方式。

总结

Rust 和 go 这两哥们都不支持基于继承的面向对象,都是基于组合的形式,主要是因为rust的设计理念不同。
Rust选择不使用继承来实现多态,而是使用trait和泛型来实现多态性。这是因为Rust的设计目标之一是提供内存安全和无运行时开销的抽象机制。
使用继承来实现多态性的语言,如C++和Java,通常需要在运行时进行动态分派,这需要额外的运行时开销。此外,继承关系还引入了对象的大小和布局的问题。
Rust通过trait和泛型的组合提供了一种更安全、更灵活的多态性实现方式。trait是一种抽象机制,允许定义一组方法的契约,并让类型来实现这些方法。泛型允许在编写代码时引入抽象的类型参数,以便代码可以适用于不同的具体类型。这种静态的泛型多态性在编译时进行类型检查,并且没有运行时开销。
使用trait和泛型实现多态性的优势包括:

  1. 零运行时开销:Rust的trait和泛型在编译时进行静态分派,不需要额外的运行时开销。
  2. 内存安全:Rust的trait和泛型机制保证了类型安全和内存安全,避免了继承层次带来的一些问题,如对象切片的大小和布局问题。
  3. 更灵活的抽象:使用trait和泛型,可以根据需要定义和使用不同的行为集合,而不受固定的继承关系的限制。

综上,Rust选择使用trait和泛型来实现多态性,以提供更安全、更高效、更灵活的抽象机制,并符合Rust的设计目标和哲学。