动态类型系统,多态通过鸭子类型(duck typing)实现;而对于静态类型系统,多态可以通过参数多态(parametric polymorphism)、特设多态(adhoc polymorphism)和子类型多态(subtype polymorphism)实现。
image.png
image.png
image.png
image.png
按类型定义、检查以及检查时能否被推导出来,Rust 是强类型 + 静态类型 + 显式类型。

参数多态通过泛型来支持

  1. // 泛型数据结构
  2. enum Option<T> {
  3. Some(T),
  4. None,
  5. }
  6. pub enum Cow<'a, B: ?Sized + 'a> where B: ToOwned,
  7. {
  8. // 借用的数据
  9. Borrowed(&'a B),
  10. // 拥有的数据
  11. Owned(<B as ToOwned>::Owned),
  12. }
  13. // 泛型函数
  14. fn id(x: T) -> T { return x;}

特设多态通过 trait 来支持

基本 trait

  1. pub trait Parse {
  2. fn parse(s: &str) -> Self;
  3. }
  4. impl Parse for T
  5. where
  6. T: FromStr + Default,
  7. {
  8. fn parse(s: &str) -> Self {
  9. todo!()
  10. }
  11. }

带关联类型的 trait

这里必须限制 Self为 Sized,可能与 Result<T, E>中的 T 需要是 move 的,所以必须知道其大小;

  1. pub trait Parse {
  2. type Error;
  3. fn parse(s: &str) -> Result<Self, Self::Error>
  4. where
  5. Self: Sized;
  6. }

支持泛型的 trait

  1. pub trait Add<Rhs = Self> {
  2. type Output;
  3. #[must_use]
  4. fn add(self, rhs: Rhs) -> Self::Output;
  5. }
  1. use std::ops::Add;
  2. #[derive(Debug)]
  3. struct Complex {
  4. real: f64,
  5. imagine: f64,
  6. }
  7. impl Complex {
  8. pub fn new(real: f64, imagine: f64) -> Self {
  9. Self { real, imagine }
  10. }
  11. }
  12. // 对 Complex 类型的实现
  13. impl Add for Complex {
  14. type Output = Self;
  15. // 注意 add 第一个参数是 self,会移动所有权
  16. fn add(self, rhs: Self) -> Self::Output {
  17. let real = self.real + rhs.real;
  18. let imagine = self.imagine + rhs.imagine;
  19. Self::new(real, imagine)
  20. }
  21. }
  22. fn main() {
  23. let c1 = Complex::new(1.0, 1f64);
  24. let c2 = Complex::new(2 as f64, 3.0);
  25. println!("{:?}", c1 + c2);
  26. // c1、c2 已经被移动,所以下面这句无法编译
  27. // println!("{:?}", c1 + c2);
  28. }
  29. // ...
  30. // 如果不想移动所有权,可以为 &Complex 实现 add,这样可以做 &c1 + &c2
  31. impl Add for &Complex {
  32. // 注意返回值不应该是 Self 了,因为此时 Self 是 &Complex
  33. type Output = Complex;
  34. fn add(self, rhs: Self) -> Self::Output {
  35. let real = self.real + rhs.real;
  36. let imagine = self.imagine + rhs.imagine;
  37. Complex::new(real, imagine)
  38. }
  39. }
  40. fn main() {
  41. let c1 = Complex::new(1.0, 1f64);
  42. let c2 = Complex::new(2 as f64, 3.0);
  43. println!("{:?}", &c1 + &c2);
  44. println!("{:?}", c1 + c2);
  45. }

不使用默认泛型:

  1. // ...
  2. // 因为 Add<Rhs = Self> 是个泛型 trait,我们可以为 Complex 实现 Add<f64>
  3. impl Add<f64> for &Complex {
  4. type Output = Complex;
  5. // rhs 现在是 f64 了
  6. fn add(self, rhs: f64) -> Self::Output {
  7. let real = self.real + rhs;
  8. Complex::new(real, self.imagine)
  9. }
  10. }
  11. fn main() {
  12. let c1 = Complex::new(1.0, 1f64);
  13. let c2 = Complex::new(2 as f64, 3.0);
  14. println!("{:?}", &c1 + &c2);
  15. println!("{:?}", &c1 + 5.0);
  16. println!("{:?}", c1 + c2);
  17. }

经典例子:tower::Service

  1. // Service trait 允许某个 service 的实现能处理多个不同的 Request
  2. pub trait Service<Request> {
  3. type Response;
  4. type Error;
  5. // Future 类型受 Future trait 约束
  6. type Future: Future;
  7. fn poll_ready(
  8. &mut self,
  9. cx: &mut Context<'_>
  10. ) -> Poll<Result<(), Self::Error>>;
  11. fn call(&mut self, req: Request) -> Self::Future;
  12. }

image.png
trait 继承
以 StreamExt 为例,由于 StreamExt 中的方法都有缺省的实现,且所有实现了 Stream trait 的类型都实现了 StreamExt:

  1. impl<T: ?Sized> StreamExt for T where T: Stream {}

子类型多态可以用 trait object 来支持

严格意义上说,子类型多态是面向对象语言的专利。如果一个对象 A 是对象 B 的子类,那么 A 的实例可以出现在任何期望 B 的实例的上下文中,比如猫和狗都是动物,如果一个函数的接口要求传入一个动物,那么传入猫和狗都是允许的。

  1. pub trait Formatter {
  2. fn format(&self, input: &mut String) -> bool;
  3. }
  4. struct MarkdownFormatter;
  5. impl Formatter for MarkdownFormatter {
  6. fn format(&self, input: &mut String) -> bool {
  7. input.push_str("\nformatted with Markdown formatter");
  8. true
  9. }
  10. }
  11. struct RustFormatter;
  12. impl Formatter for RustFormatter {
  13. fn format(&self, input: &mut String) -> bool {
  14. input.push_str("\nformatted with Rust formatter");
  15. true
  16. }
  17. }
  18. struct HtmlFormatter;
  19. impl Formatter for HtmlFormatter {
  20. fn format(&self, input: &mut String) -> bool {
  21. input.push_str("\nformatted with HTML formatter");
  22. true
  23. }
  24. }
  25. pub fn format(input: &mut String, formatters: Vec<&dyn Formatter>) {
  26. for formatter in formatters {
  27. formatter.format(input);
  28. }
  29. }
  30. fn main() {
  31. let mut text = "Hello world!".to_string();
  32. let html: &dyn Formatter = &HtmlFormatter;
  33. let rust: &dyn Formatter = &RustFormatter;
  34. let formatters = vec![html, rust];
  35. format(&mut text, formatters);
  36. println!("text: {}", text);
  37. }

Trait Object 的实现机理

image.png
HtmlFormatter 的引用赋值给 Formatter 后,会生成一个 Trait Object,在上图中可以看到,Trait Object 的底层逻辑就是胖指针。其中,一个指针指向数据本身,另一个则指向虚函数表(vtable)。

image.png对象安全(object safety)

只有满足对象安全的 trait 才能使用 trait object,在官方文档中有详细讨论。

如果 trait 所有的方法,返回值是 Self 或者携带泛型参数,那么这个 trait 就不能产生 trait object

  • 不允许返回 Self,是因为 trait object 在产生时,原来的类型会被抹掉,所以 Self 是谁是不知道的。

    1. pub fn format(input: &mut String, formatters: Vec<&dyn Formatter>) {
    2. for formatter in formatters {
    3. // 其实也是可以匹配到当前的 formatter 的具体类型,但是无法在编译期获取到,所以是不确定的
    4. formatter.format(input);
    5. }
    6. }
  • 不允许使用携带泛型参数,是因为 rust 里带泛型的类型在编译时会做单态化,而 trait object 试运行时的产物,两者不能兼容。

小结

image.png