动态类型系统,多态通过鸭子类型(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 T
where
T: 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>
where
Self: 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 + &c2
impl Add for &Complex {
// 注意返回值不应该是 Self 了,因为此时 Self 是 &Complex
type 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 的实现能处理多个不同的 Request
pub 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 试运行时的产物,两者不能兼容。