多态
如果你有其它语言的学习经验,可能常常听说多态,而多态分为静态多态和动态多态。这可以说是计算机界的元老级别八股文了,面试常考。
但是作为多范式语言,希望基于Rust带大家理解更加广泛的多态体系,而不是以简单的动静区分。
在类型系统里,多态性通常分为三类:
参数多态(Parametric Polymorphism):属于静态多态,函数或类型对任意类型参数都能工作,不依赖具体实现。
Rust的泛型,impl traitC++的模板
特设多态(Ad-hoc Polymorphism):属于静态多态,同一个函数名在不同类型上有不同实现。
Rust的TraitHaskell的typeclassC++函数重载
子类型多态(Subtype Polymorphism):属于动态多态,面向对象语言里常见,子类可以替代父类使用。
Rust的Trait对象C++的虚函数Java的继承
1.参数多态
通过 泛型 或 impl Trait 实现,编译期静态分发。
例如:
// 泛型写法
fn swap<T>(a: &mut T, b: &mut T) {
std::mem::swap(a, b);
}
// impl Trait 写法
fn print_debug(x: impl std::fmt::Debug) {
println!("{:?}", x);
}这种多态在编译期可以传入多种类型,并且每种类型最后的实现都是相同的,根据调用来进行单态化,从而产生符合各个类型的函数版本。
2.特设多态
通过 trait 为不同类型提供不同实现,本质就是函数重载。
trait Hello {
fn hello(&self);
}
impl Hello for i32 {
fn hello(&self) {
println!("i32 says hello: {}", self);
}
}
impl Hello for String {
fn hello(&self) {
println!("String says hello: {}", self);
}
}
fn main() {
let num = 666;
let s = String::from("hello");
num.hello(); // i32 says hello: 666
s.hello(); // String says hello: hello
}虽然Rust没有提供直接的函数重载,但是Trait可以看成一种函数重载。
比如String和i32都能调用hello这个函数,可以理解为在hello这个同名函数中,你可以给第一个参数传入不同的self,从而调用到不同版本的函数。函数名相同,而根据不同参数来选择版本,这其实就是函数重载。
3.子类型多态
通过 trait object 实现,运行时动态分发。
trait Shape {
fn area(&self) -> f64;
}
struct Circle { r: f64 }
struct Square { a: f64 }
impl Shape for Circle {
fn area(&self) -> f64 { 3.14 * self.r * self.r }
}
impl Shape for Square {
fn area(&self) -> f64 { self.a * self.a }
}
fn print_area(s: &dyn Shape) {
println!("area = {}", s.area());
}Rust是一个混合范式语言,而不是一个面向对象语言,但是Rust汲取了面向对象的核心思想,把他们最重要的一些理论抽出来,做符合Rust的表达。
在Rust中,几乎没有别的位置有对象这样的OOP风格表述。唯独dyn Trait这里使用了特征对象的表述,就是因为这里融合了非常类似于面向对象的多态思想。并且是以C++风格为主的多态思想。
你也许困惑为什么要叫虚表,为什么叫虚指针,为什么叫析构函数。对Rust来说,其实就是从C++中借鉴后,做出了一定的机制修改。
这里可以理解为Trait Object是一种父类型,而所有实现了Trait的类型都是子类型。父类型的引用或者指针,可以接收子类型的实例作为参数,然后运行时动态调用方法。因此 dyn Trait 从思想上就是一个子类型多态。
面向对象三大思想继承、多态、封装。
首先Rust舍弃了继承,采用组合优先的思想。但是也有Trait继承这样的语法,以及刚才说的 dyn Trait 融合了继承的思想在里面。
在多态中,Rust有丰富全面的多态体系,你可以舍弃性能换取灵活性,你也可以舍弃灵活性以获取最高的性能,这一切都基于强大的Trait体系,它的知识点最庞杂。
对于封装来说,Rust有合理的函数封装体系,pub控制可见性,这一块知识点也会在后续讲解。
可见的是,Rust虽然不支持面向对象,但是其最核心,最精华的思想都已被Rust收入囊中。
