关于 m04-zero-cost
m04-zero-cost 是帮助 Rust 开发者选择类型抽象方式的学习技能。它从分派、性能和编译时间等权衡比较泛型、Trait 对象与枚举。
主要功能
- 01选择抽象层级的提示
- 02静态与动态分派比较
- 03泛型、Trait 对象和枚举指南
- 04对象安全检查
- 05Rust 错误代码指南
适用场景
- 01为性能敏感代码选择分派方式
- 02设计异构集合
- 03评估插件架构中的类型方案
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ actionbook/ rust-skills --skill m04-zero-cost安装后在 Claude Code 中输入/m04-zero-cost确认已加载。
详细介绍
零成本抽象
第 1 层:语言机制
核心问题
我们需要编译时多态还是运行时多态?
在选择泛型和 trait 对象之前:
- 类型在编译时已知吗?
- 是否需要异构集合?
- 性能优先级是什么?
错误 → 设计问题
| 错误 | 别只说 | 不妨问问 |
|---|---|---|
| E0277 | “添加 trait 约束” | 这个抽象处于合适的层级吗? |
| E0308 | “修正类型” | 应该统一类型还是保持类型不同? |
| E0599 | “导入 trait” | 这个 trait 是合适的抽象吗? |
| E0038 | “使其满足对象安全” | 我们真的需要动态分发吗? |
思考提示
添加 trait 约束之前:
-
需要什么抽象?
- 行为相同,类型不同 → trait
- 行为不同,类型相同 → enum
- 不需要抽象 → 具体类型
-
何时才能确定类型?
- 编译时 → 泛型(静态分发)
- 运行时 → trait 对象(动态分发)
-
优先考虑哪些权衡?
- 性能 → 泛型
- 编译时间 → trait 对象
- 灵活性 → 视情况而定
向上追溯 ↑
类型系统阻碍开发时:
E0277 (trait bound not satisfied)
↑ Ask: Is the abstraction level correct?
↑ Check: m09-domain (what behavior is being abstracted?)
↑ Check: m05-type-driven (should use newtype?)
| 持续出现的错误 | 追溯到 | 问题 |
|---|---|---|
| 复杂的 trait 约束 | m09-domain | 抽象合适吗? |
| 对象安全问题 | m05-type-driven | 类型状态能帮上忙吗? |
| 类型爆炸 | m10-performance | 能接受 dyn 的开销吗? |
向下追溯 ↓
从设计到实现:
"Need to abstract over types with same behavior"
↓ Types known at compile time → impl Trait or generics
↓ Types determined at runtime → dyn Trait
"Need collection of different types"
↓ Closed set → enum
↓ Open set → Vec<Box<dyn Trait>>
"Need to return different types"
↓ Same type → impl Trait
↓ Different types → Box<dyn Trait>
快速参考
| 模式 | 分发方式 | 代码体积 | 运行时开销 |
|---|---|---|---|
fn foo<T: Trait>() | 静态 | +膨胀 | 零 |
fn foo(x: &dyn Trait) | 动态 | 最小 | vtable 查找 |
impl Trait 返回值 | 静态 | +膨胀 | 零 |
Box<dyn Trait> | 动态 | 最小 | 分配 + vtable |
语法对比
// Static dispatch - type known at compile time
fn process(x: impl Display) { } // argument position
fn process<T: Display>(x: T) { } // explicit generic
fn get() -> impl Display { } // return position
// Dynamic dispatch - type determined at runtime
fn process(x: &dyn Display) { } // reference
fn process(x: Box<dyn Display>) { } // owned
错误代码参考
| 错误 | 原因 | 快速修复 |
|---|---|---|
| E0277 | 类型未实现 trait | 添加 impl 或更改约束 |
| E0308 | 类型不匹配 | 检查泛型参数 |
| E0599 | 未找到方法 | 使用 use 导入 trait |
| E0038 | Trait 不满足对象安全 | 使用泛型或重新设计 |
决策指南
| 场景 | 选择 | 原因 |
|---|---|---|
| 性能关键 | 泛型 | 零运行时开销 |
| 异构集合 | dyn Trait | 运行时类型不同 |
| 插件架构 | dyn Trait | 编译时类型未知 |
| 缩短编译时间 | dyn Trait | 减少单态化 |
| 类型集合小且已知 | enum | 无间接寻址 |
对象安全
trait 满足以下条件时,即为对象安全:
- 不带
Self: Sized约束 - 不返回
Self - 不包含泛型方法
- 对非对象安全方法使用
where Self: Sized
反模式
| 反模式 | 不妥之处 | 更好的做法 |
|---|---|---|
| 所有东西都过度泛型化 | 编译时间、复杂性 | 尽可能使用具体类型 |
对已知类型使用 dyn | 不必要的间接寻址 | 泛型 |
| 复杂的 trait 层级结构 | 难以理解 | 简化设计 |
| 忽略对象安全 | 限制灵活性 | 有需要时规划使用 dyn |
相关技能
| 情况 | 查看 |
|---|---|
| 类型驱动设计 | m05-type-driven |
| 领域抽象 | m09-domain |
| 性能问题 | m10-performance |
| Send/Sync 约束 | m07-concurrency |
评论
支持 Markdown