动态类型系统,多态通过鸭子类型(duck typing)实现;而对于静态类型系统,多态可以通过参数多态(parametric polymorphism)、特设多态(adhoc polymorphism)和子类型多态(subtype polymorphism)实现。



按类型定义、检查以及检查时能否被推导出来,Rust 是强类型 + 静态类型 + 显式类型。
参数多态通过泛型来支持
// 泛型数据结构enum Option<T> {Some(T),None,}pub enum Cow<'a, B: ?Sized + 'a> where B: ToOwned,{// 借用的数据Borrowed(&'a B),// 拥有的数据Owned(<B as ToOwned>::Owned),}// 泛型函数fn id(x: T) -> T { return x;}
特设多态通过 trait 来支持
基本 trait
pub trait Parse {fn parse(s: &str) -> Self;}impl Parse for TwhereT: FromStr + Default,{fn parse(s: &str) -> Self {todo!()}}
带关联类型的 trait
这里必须限制 Self为 Sized,可能与 Result<T, E>中的 T 需要是 move 的,所以必须知道其大小;
pub trait Parse {type Error;fn parse(s: &str) -> Result<Self, Self::Error>whereSelf: Sized;}
支持泛型的 trait
pub trait Add<Rhs = Self> {type Output;#[must_use]fn add(self, rhs: Rhs) -> Self::Output;}
use std::ops::Add;#[derive(Debug)]struct Complex {real: f64,imagine: f64,}impl Complex {pub fn new(real: f64, imagine: f64) -> Self {Self { real, imagine }}}// 对 Complex 类型的实现impl Add for Complex {type Output = Self;// 注意 add 第一个参数是 self,会移动所有权fn add(self, rhs: Self) -> Self::Output {let real = self.real + rhs.real;let imagine = self.imagine + rhs.imagine;Self::new(real, imagine)}}fn main() {let c1 = Complex::new(1.0, 1f64);let c2 = Complex::new(2 as f64, 3.0);println!("{:?}", c1 + c2);// c1、c2 已经被移动,所以下面这句无法编译// println!("{:?}", c1 + c2);}// ...// 如果不想移动所有权,可以为 &Complex 实现 add,这样可以做 &c1 + &c2impl Add for &Complex {// 注意返回值不应该是 Self 了,因为此时 Self 是 &Complextype Output = Complex;fn add(self, rhs: Self) -> Self::Output {let real = self.real + rhs.real;let imagine = self.imagine + rhs.imagine;Complex::new(real, imagine)}}fn main() {let c1 = Complex::new(1.0, 1f64);let c2 = Complex::new(2 as f64, 3.0);println!("{:?}", &c1 + &c2);println!("{:?}", c1 + c2);}
不使用默认泛型:
// ...// 因为 Add<Rhs = Self> 是个泛型 trait,我们可以为 Complex 实现 Add<f64>impl Add<f64> for &Complex {type Output = Complex;// rhs 现在是 f64 了fn add(self, rhs: f64) -> Self::Output {let real = self.real + rhs;Complex::new(real, self.imagine)}}fn main() {let c1 = Complex::new(1.0, 1f64);let c2 = Complex::new(2 as f64, 3.0);println!("{:?}", &c1 + &c2);println!("{:?}", &c1 + 5.0);println!("{:?}", c1 + c2);}
经典例子:tower::Service
// Service trait 允许某个 service 的实现能处理多个不同的 Requestpub trait Service<Request> {type Response;type Error;// Future 类型受 Future trait 约束type Future: Future;fn poll_ready(&mut self,cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>>;fn call(&mut self, req: Request) -> Self::Future;}

trait 继承
以 StreamExt 为例,由于 StreamExt 中的方法都有缺省的实现,且所有实现了 Stream trait 的类型都实现了 StreamExt:
impl<T: ?Sized> StreamExt for T where T: Stream {}
子类型多态可以用 trait object 来支持
严格意义上说,子类型多态是面向对象语言的专利。如果一个对象 A 是对象 B 的子类,那么 A 的实例可以出现在任何期望 B 的实例的上下文中,比如猫和狗都是动物,如果一个函数的接口要求传入一个动物,那么传入猫和狗都是允许的。
pub trait Formatter {fn format(&self, input: &mut String) -> bool;}struct MarkdownFormatter;impl Formatter for MarkdownFormatter {fn format(&self, input: &mut String) -> bool {input.push_str("\nformatted with Markdown formatter");true}}struct RustFormatter;impl Formatter for RustFormatter {fn format(&self, input: &mut String) -> bool {input.push_str("\nformatted with Rust formatter");true}}struct HtmlFormatter;impl Formatter for HtmlFormatter {fn format(&self, input: &mut String) -> bool {input.push_str("\nformatted with HTML formatter");true}}pub fn format(input: &mut String, formatters: Vec<&dyn Formatter>) {for formatter in formatters {formatter.format(input);}}fn main() {let mut text = "Hello world!".to_string();let html: &dyn Formatter = &HtmlFormatter;let rust: &dyn Formatter = &RustFormatter;let formatters = vec![html, rust];format(&mut text, formatters);println!("text: {}", text);}
Trait Object 的实现机理

HtmlFormatter 的引用赋值给 Formatter 后,会生成一个 Trait Object,在上图中可以看到,Trait Object 的底层逻辑就是胖指针。其中,一个指针指向数据本身,另一个则指向虚函数表(vtable)。
对象安全(object safety)
只有满足对象安全的 trait 才能使用 trait object,在官方文档中有详细讨论。
如果 trait 所有的方法,返回值是 Self 或者携带泛型参数,那么这个 trait 就不能产生 trait object。
不允许返回 Self,是因为 trait object 在产生时,原来的类型会被抹掉,所以 Self 是谁是不知道的。
pub fn format(input: &mut String, formatters: Vec<&dyn Formatter>) {for formatter in formatters {// 其实也是可以匹配到当前的 formatter 的具体类型,但是无法在编译期获取到,所以是不确定的formatter.format(input);}}
不允许使用携带泛型参数,是因为 rust 里带泛型的类型在编译时会做单态化,而 trait object 试运行时的产物,两者不能兼容。
小结

