0x00 回顾与开篇
多态性(Polymorphism)在很多语言中都存在,比如Java/C#等。有了编程语言的多态性会使我们在工作中更加灵活和方便。当然,Rust也有多态性的特点。在Rust中有三种主要方法来实现多态,我们的目的就是讨论每种方法的优缺点。上一篇文章介绍了Trait的两种方式。今天咱们一起来看最后一种方式——Enum+Struct。
0x01 Enum + Struct
废话不多说,直接上代码。
enum ShapeEnum {
Rectangle(Rectangle),
Triangle(Triangle),
Circle(Circle)
}
struct Rectangle { pub width: f32, pub height: f32 }
struct Triangle { pub side: f32 }
struct Circle { pub radius: f32 }
trait Shape {
fn perimeter(&self) -> f32;
fn area(&self) -> f32;
}
impl Shape for ShapeEnum {
fn perimeter(&self) -> f32 {
match self {
ShapeEnum::Rectangle(rect) => rect.perimeter(),
ShapeEnum::Triangle(tri) => tri.perimeter(),
ShapeEnum::Circle(circ) => circ.perimeter(),
}
}
fn area(&self) -> f32 {
match self {
ShapeEnum::Rectangle(rect) => rect.area(),
ShapeEnum::Triangle(tri) => tri.area(),
ShapeEnum::Circle(circ) => circ.area(),
}
}
}
impl Shape for Rectangle {
fn perimeter(&self) -> f32 {
self.width * 2.0 + self.height * 2.0
}
fn area(&self) -> f32 {
self.width * self.height
}
}
impl Shape for Triangle {
fn perimeter(&self) -> f32 {
self.side * 3.0
}
fn area(&self) -> f32 {
self.side * 0.5 * 3.0_f32.sqrt() / 2.0 * self.side
}
}
impl Shape for Circle {
fn perimeter(&self) -> f32 {
self.radius * 2.0 * std::f32::consts::PI
}
fn area(&self) -> f32 {
self.radius * self.radius * std::f32::consts::PI
}
}
这种写法,在我看来应该是比较不错的一种了。但是它有个缺点,每当你需要添加新的变量和方法时,需要在很多地方添加代码。其它为自己的结构体实现 Shape
的 crate
将无法将它们传递给 ShapeEnum
,维护比较麻烦。
0x02 推荐使用一种方式?
在这里,我们通过一个表格来对比下以上三种方式优缺点吧。(表格仅供参考)
内联布局 | 不浪费内存 | 混合类型集合 | 扩展性 | 易于编写和维护 | |
---|---|---|---|---|---|
枚举 | ✅ | ❌ | ✅ | ❌ | ✅ |
Trait泛型 | ✅ | ✅ | ❌ | ✅ | ✅ |
Trait动态 | ❌ | ✅ | ✅ | ✅ | ✅ |
枚举+结构体 | ✅ | ✅ | ✅ | ✅ | ❌ |
上面的表格仅代表个人看法。在工作中,如果变量很小,已知,有限且没有给其它人提供 crate
的需要。我还是比较建议使用枚举。另外,动态调度还是比较推荐使用的,即使它的性能较低,但大多数时候它仍然表现的还是不错的。当然泛型在很多语言中都存在,如果你熟悉了Java/C#等面向对象的语言,我也是比较推荐的。
0x03 小结
我通过3篇文章啰里啰嗦的介绍了Rust中实现多态的3种方式。可能读到这里,有些同学还在纠结我应该使用哪种方式。其实,在我们平时的大多数情况下使用哪一种并不重要,选择自己喜欢的并且适合自己所应用的场景就可以了,并不要被别人的任何言论所束缚,除非你真的是非要挤出那一点性能,我认为Rust的性能已经足够好了。
最近工作比较繁忙,更新有些慢,望大家见谅了!