直接回答:embedded-hal 为 MCU 外设定义一套标准 trait(GPIO 的 OutputPin/InputPin、SPI/I2C/UART 总线、定时器 Delay 等),把生态切成三层:芯片厂商的 HAL 库实现 trait,驱动作者(传感器、屏幕)针对 trait 写泛型代码,应用层自由组合——驱动写一次跑在所有实现 trait 的芯片上,解决了嵌入式长期“驱动绑定芯片”的碎片问题。
展开解析:设计细节看 SPI 最有代表性:早期 trait 按“单字节传输”建模,DMA 与硬件队列优势全丢,1.0 改为事务模型(transaction:一次调用内多段 transfer/write,CS 全程拉低),既能映射到硬件 FIFO 也兼容软 SPI——抽象粒度选“事务”而非“字节”是关键决策。错误处理统一 associated type Error,各 HAL 用具体错误类型,驱动只要求可比较或转换,避免装箱成本。零成本手段:trait 泛型单态化后内联,生成的机器码与手写寄存器操作等价;Pin 状态编译期编码(例如 stm32 系列的 mode 类型参数),配置错误编译期拦。可移植性的真实边界:trait 只覆盖通用能力,芯片特性(特殊 SPI 模式、硬件 CRC)仍需 escape hatch 或回退到具体 HAL 类型;驱动对时序敏感时泛型代码未必满足,需要特性检测或专用路径。异步化是进行中方向:embedded-hal-async 补 Async traits 配 embassy 生态。学习价值:它是 Rust trait 系统做“生态级接口标准化”的最佳样本。
追问方向:为什么不用 dyn trait 对象?总线共享(多个设备挂同一 SPI)如何处理?
(约 470 字)