跳转到内容

第 8 章 多态与架构选型——静态分发、动态分发与 OOP

在前七章中,我们建立了一整套编译期静态确定的模型:变量的大小在编译期算死、释放时机在作用域末尾静态插入、泛型 <T: Trait> 在编译期通过单态化生成多份专属机器码(静态分发)。

但当程序进入更复杂的动态业务场景——例如函数的返回值类型取决于运行时的用户输入,或者一个 Vec 数组需要同时装入十几种实现了同一个 Trait 的不同结构体时——编译期单态化就会立刻撞上它的物理边界。

本章将带你跨过这道从「编译期静态」通往「运行时动态」的分水岭,按四步推进:

  1. 静态与动态的全局大局观(impl Trait 与单态化的两道边界):掌握 impl Trait 在参数与返回值位置的语法,亲历静态单态化在「运行时分支返回不同类型」与「异构集合」面前的报错,从而拉通全书「编译期静态结算 vs 运行时动态结算」的全局切换开关。
  2. 动态分发与 dyn Trait 虚表胖指针:迎来与第 4 章切片 &[T] 的第二次胖指针会师——看清为什么擦除具体类型后的 &dyn Trait 在栈上占 16 字节(data_ptr + vtable_ptr),以及 CPU 如何在运行时通过虚表(vtable)查表调用方法。
  3. 静态分发 vs 动态分发正面交锋与 dyn 兼容性:全方位对比 <T: Trait> 与 &dyn Trait 的内存布局、代码体积、内联优化与调用成本,并从虚表物理结构直接推导出为什么有的 Trait 不能写成 dyn Trait(dyn 兼容性)。
  4. 没有 class 和 extends,如何组织代码与选型多态?:拆解传统面向对象 class 的三合一包袱,用组合与 Trait 替代继承,并立下工程与代码审查(Code Review)中的终极多态选型铁律——闭合集合用 enum(ADT),开放集合用泛型或 dyn Trait。

读完本章,检验自己的四个问题

Section titled “读完本章,检验自己的四个问题”
  • 为什么 fn make(flag: bool) -> impl Summary 在 if 和 else 分支返回两个都实现了 Summary 的不同结构体时会编译失败?这暴露了编译期静态单态化的什么物理限制?
  • 为什么 &Article 在 64 位平台上占 8 字节,而转成 &dyn Summary 后立刻变成了 16 字节?多出来的那 8 个字节指向哪里、里面存着什么?
  • 为什么一个包含 fn clone_self(&self) -> Self 或泛型方法 fn process<U>(&self, x: U) 的 Trait,编译器禁止将它写成 &dyn Trait?
  • 在设计系统架构或审查 AI 生成的代码时,面对「多种业务形态共享行为」的需求,你依据什么标准在 enum、泛型 <T: Trait> 与 Box<dyn Trait> 之间做取舍?