直接回答:单态化指编译器为泛型代码的每组具体类型参数生成一份特化机器码——Vec<i32>Vec<String>push 是两份独立函数。好处是静态分发、可内联、零间接开销,这是“零成本抽象”的实现基础;代价是编译变慢、二进制膨胀,实例过多还会挤占指令缓存。

展开解析:与 dyn Trait 的对比即“代码份数 vs 间接调用”:dyn 只保留一份代码、经 vtable 间接调用,省体积但阻碍内联。热路径小函数用泛型,大型冷路径函数可用 dyn 收敛体积。标准库有经典技巧“泛型外壳 + 非泛型内核”:把类型无关的逻辑下沉到类型擦除的内部函数(hashbrown 把表操作建立在原始指针上),让各类型组合共享绝大部分机器码。另一个坑是泛型递归:每层类型都不同的写法会让编译器无限实例化,需在某层转为 dyn 打断。分析膨胀可用 cargo bloat 定位大头。

// 泛型外壳:每种 K/V 各一份(很薄,可内联)
pub fn get<K: Hash, V>(map: &Map<K, V>, k: &K) -> Option<&V> {
    map.inner.get_raw(k.hash()) // 内核按指针工作,全类型共享一份
}

追问方向:为什么内联与单态化互相成就?跨动态库边界时泛型在哪一方实例化?dyn 与泛型混用时如何权衡? (约 480 字)