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 推荐使用一种方式?
在这里,我们通过一个表格来对比下以上三种方式优缺点吧。(表格仅供参考)
上面的表格仅代表个人看法。在工作中,如果变量很小,已知,有限且没有给其他人提供 crate
的需要。我还是比较建议使用枚举。另外,动态调度还是比较推荐使用的,即使它的性能较低,但大多数时候它仍然表现的还是不错的。当然泛型在很多语言中都存在,如果你熟悉了Java/C#等面向对象的语言,我也是比较推荐的。
0x03 小结
我通过3篇文章啰里啰嗦的介绍了Rust中实现多态的3种方式。可能读到这里,有些同学还在纠结我应该使用哪种方式。其实,在我们平时的大多数情况下使用哪一种并不重要,选择自己喜欢的并且适合自己所应用的场景就可以了,并不要被别人的任何言论所束缚,除非你真的是非要挤出那一点性能,我认为Rust的性能已经足够好了。