[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fktie8zciNwg7h0baGzhE8E9T-PBGbEgOigHMgPEjPOU":3},{"item":4,"related":48},{"id":5,"type":6,"title":7,"slug":8,"summary":9,"body":10,"coverUrl":11,"productScreenshots":12,"productLinks":13,"authorName":14,"authorUrl":15,"authorSubject":16,"category":17,"tags":22,"sourceLabel":39,"sourceName":40,"sourceUrl":41,"status":42,"seoTitle":7,"seoDescription":9,"canonicalUrl":39,"isFeatured":43,"sno":44,"sortOrder":45,"publishedAt":46,"updatedAt":47,"createdAt":47},"519292f5-a8e8-4803-ab60-03ba66ee2d3e","article","Monomorphization：泛型代码为什么会被编译成很多份？","monomorphization-rust-generics-explained","Monomorphization 会为实际使用到的具体类型生成专门代码，让泛型在运行时更接近手写实现，但也可能增加编译时间和二进制体积。本文用模板机器解释 Rust 的单态化及其工程取舍。","泛型函数像一份可以处理多种材料的模板：同一个排序函数既可以处理整数，也可以处理字符串。但 CPU 执行的不是“泛型”，而是已经确定了数据布局和调用方式的机器代码。Monomorphization，中文常译为“单态化”，就是在编译时为实际使用到的具体类型生成专门版本。\n\n## 一个泛型函数如何变成多份代码\n\n假设程序使用了 `Vec\u003Cu64>` 和 `Vec\u003CString>`。编译器会收集这些具体实例，并为相关操作生成针对 `u64` 与 `String` 的代码。之后，优化器可以知道元素大小、移动方式和调用目标，省去部分运行时分派。\n\n这也是 Rust 静态分发效率高的原因之一。泛型函数在源代码里只写一份，但生成的中间表示和机器代码可能有多份。编译器开发指南把这一步放在后端代码生成之前，并且会把具体实例收集成待编译项目。\n\n## 性能收益和成本\n\n单态化让编译器更容易内联函数、消除抽象开销，并利用具体类型做优化。代价是：类型组合越多，生成的实例越多，编译时间和二进制体积也可能增加。大型项目会通过增量编译、代码生成单元、共享泛型实例等方式缓解。\n\n这和 Java 的类型擦除形成鲜明对比。擦除倾向于共享一份运行时代码，单态化倾向于生成更专门的代码。没有“永远更好”的选择，关键在于项目更在意运行时性能、编译速度、包体积还是跨语言兼容。\n\n## 对 AI 编程的启发\n\nAI 可能为了追求“泛化”而设计很多层泛型，也可能复制出大量近似实现。理解单态化后，开发者会更关注泛型的实际实例数量：哪些类型真的会用到？抽象是否值得它带来的编译成本？是否应改用动态分发或共享实现？\n\n## 读者应该记住\n\nMonomorphization 是把“一份泛型模板”编译成“多份具体机器代码”的过程。它把抽象成本从运行时挪到编译时，换来更直接的执行路径，同时也需要控制代码膨胀。\n\n资料：[Rust Compiler Development Guide：Monomorphization](https:\u002F\u002Frustc-dev-guide.rust-lang.org\u002Fbackend\u002Fmonomorph.html)","\u002Fuploads\u002F2026-09-20\u002Fc27ae790-081a-4da2-a939-17315fdfbd91.jpg",[],[],"Foundit","https:\u002F\u002Ffoundit.cn","foundit-ai-editorial",{"id":18,"name":19,"slug":20,"description":21},"6179d3b6-dc34-4483-9ded-3cd9f1b37a47","科普","abbreviation","介绍各领域新兴概念",[23,27,31,35],{"id":24,"name":25,"slug":26},"144abe77-0dc6-4f66-a176-20bddb1c0bfa","编程","coding",{"id":28,"name":29,"slug":30},"a202d639-99a6-488a-a712-4d4c6ffd7e15","开发","dev",{"id":32,"name":33,"slug":34},"7c76bfc2-f80f-4ee0-a95d-27bd8708b434","技术","slug",{"id":36,"name":37,"slug":38},"4c2bbea6-eab7-40a8-8447-1de478ff7749","分析","analyse",null,"Rust Compiler Development Guide：Monomorphization","https:\u002F\u002Frustc-dev-guide.rust-lang.org\u002Fbackend\u002Fmonomorph.html","published",false,53,0,"2026-09-20T00:00:00.000Z","2026-09-20T03:57:59.501Z",[49,57,65],{"id":50,"type":6,"title":51,"slug":52,"summary":53,"coverUrl":54,"authorName":14,"sno":55,"publishedAt":46,"createdAt":56},"d0700249-defa-43a1-a66a-4455c6889072","ABI：为什么源码能编译，二进制却不能互相调用？","application-binary-interface-abi-explained","ABI 是二进制世界的调用合同，规定参数传递、对象布局、符号命名和异常处理。本文区分 ABI 与 API，解释动态库、C++ 兼容性和跨语言绑定为什么不能只看函数签名。","\u002Fuploads\u002F2026-09-20\u002F33666a48-4e8f-42a3-9ffd-f4c490498689.jpg",42,"2026-09-20T03:58:01.058Z",{"id":58,"type":6,"title":59,"slug":60,"summary":61,"coverUrl":62,"authorName":14,"sno":63,"publishedAt":46,"createdAt":64},"c9f4e936-9533-4b9c-a1de-f03ef09fed37","WAL：为什么数据库要先写日志，再写真正数据？","write-ahead-logging-wal-database-explained","WAL 要求描述数据变化的日志先于数据页持久化，让数据库可以延迟刷写并在崩溃后通过重放恢复。本文用账本和收据解释 REDO、检查点、复制与持久性设置的关系。","\u002Fuploads\u002F2026-09-20\u002F6260c953-345b-4c9d-b3d6-6202c7e5539c.jpg",43,"2026-09-20T03:58:15.082Z",{"id":66,"type":6,"title":67,"slug":68,"summary":69,"coverUrl":70,"authorName":14,"sno":71,"publishedAt":46,"createdAt":72},"41d50774-50df-4e2f-b1b3-ab6d9f329726","Backpressure：生产者太快时，系统怎样不被数据淹没？","backpressure-reactive-streams-explained","Backpressure 让下游处理能力反过来影响上游生产速度，避免异步流水线靠无限缓存硬撑。本文用水管和阀门解释响应式流、需求信号、数据丢弃与容量设计，也说明它和普通限流的区别。","\u002Fuploads\u002F2026-09-20\u002F35c47792-3d4b-46ae-9375-c68e4c53330e.jpg",45,"2026-09-20T03:58:08.835Z"]