![]()
2024年RustNL大会结束后的第3天,一个RFC(请求评论)悄然出现。作者m-ou-se提出"外部可实现函数"——让Rust能像C语言那样做前向声明,但由其他crate来实现。这个看似简单的语法糖,最终演变成rustc编译器过去两年最复杂的工程之一。而主导开发的工程师事后统计:实际写代码的时间,不到总工时的三分之一。
一个"简单"需求,撕开Rust的深层矛盾
Rust的模块系统有个引以为傲的特性:顺序无关。你可以先调用bar()再定义它,编译器自己会理清依赖。这靠的就是全局作用域的秩序独立性。
但crate级别(crate,Rust的包管理单元)完全是另一回事。整个crate图必须构成有向无环图(DAG),A依赖B,B就不能再依赖A。这就像C语言的全局作用域——想调用未定义的函数?先写个前向声明。
m-ou-se的提案直击这个痛点:允许用extern impl fn logger() -> Logger;声明一个函数,具体实现交给别的crate。日志库声明接口,应用层注入实现,听起来干净利落。
但Rust编译器团队看到RFC后,反应出奇一致:这东西会动到编译器的核心假设。不是"加几行代码"的事,是"重新思考整个编译流程"的事。
从"我来做"到"我们一起拆"
作者本人接下了这个任务。起初他以为主要工作是解析新语法、生成中间代码。真正开工后才发现,externally implementable items(外部可实现项,简称EI)像一块多米诺骨牌——推倒第一块,后面连锁反应根本停不下来。
![]()
EI要求编译器在"还不知道函数长什么样"的时候,就要允许其他代码调用它。这打破了rustc的多个内部不变量:类型检查阶段假设所有函数体都已知,代码生成阶段假设所有符号都能解析,甚至增量编译的缓存策略都建立在"定义先于使用"的基础上。
作者开始在Zulip(Rust项目的实时聊天平台)上高频提问。不是问"这行代码怎么写",而是问"这个设计决策五年前为什么这么做"。他逐渐意识到,自己面对的不是技术难题,是组织记忆难题——系统为什么长成这样,只有当时在场的人知道。
23个协作者,横跨6个子系统
开发日志显示,这个功能的最终patch(代码补丁)涉及23位直接贡献者。他们分布在rustc的不同领域:名字解析(name resolution)、类型系统、代码生成、增量编译、错误信息、文档系统。
有个典型场景:作者发现EI和增量编译冲突。他以为要重写缓存逻辑,但@pnkfelix(增量编译维护者)在Zulip回复:"试试把EI声明标记为'永远脏'(always dirty),让缓存系统跳过它。"这个一行代码的提示,省去了两周的弯路。
另一个例子:类型检查器原本拒绝"无函数体的函数签名"。作者准备硬改,@compiler-errors(类型系统专家)指出:"借用检查器(borrow checker)也有同样需求,我们三年前讨论过通用方案。"最后双方合用一个抽象,而非各写各的补丁。
这些对话的平均响应时间是4小时。跨时区的接力——欧洲早晨的问题,北美下午有人接,亚洲深夜还有人跟进。作者后来统计,自己在Zulip上关于EI的消息超过1800条,GitHub评论400多条,而真正提交的代码行数约3000行。
nightly上的"半成品"哲学
![]()
这个功能至今仍在nightly(Rust的每日构建版)上测试,未进入稳定版。作者刻意保持了这种"未完成"状态——不是拖延,是Rust项目的工程文化。
「我们希望真实世界的反馈来验证设计。」作者在Rust Paris 2026的演讲中解释,「nightly用户会告诉我们,这个语法在宏里好不好用,和wasm(WebAssembly,一种可移植二进制指令格式)目标平台冲不冲突。」
这种耐心有代价。EI的RFC是2024年4月提交的,两年过去,稳定化时间表依然模糊。但作者认为值得:「如果 rushed(仓促)进入稳定版,发现设计缺陷,迁移成本是现在的十倍。」
对比其他语言的项目,这种"慢"显得反常。但Rust的稳定性承诺(stability without stagnation)要求:一旦进入稳定版,就要保证向后兼容。nightly是唯一的试验场,而试验需要足够长的时间收集边缘案例。
代码之外的"基础设施"
作者最后列了一组数字:EI功能涉及的文档页面12个,内部设计笔记7份,Zulip话题线程46个,视频会议记录9小时。这些不会出现在git log里,但构成了功能落地的必要条件。
他特别提到一个细节。某次视频会议中,@joshtriplett(语言团队联合负责人)问了个看似无关的问题:"如果crate A声明EI,crate B实现,crate C同时依赖A和B,谁来保证B的实现真的匹配A的声明?"这个问题引出了"一致性检查(coherence check)"的新子任务,最终由另一位志愿者@lcnr接手。
「如果没有那次会议,这个功能上线后会在特定依赖图下编译失败,而我们可能永远不知道原因。」作者写道。
现在EI功能已经可以在nightly试用。作者的最后一条Zulip消息是问:有没有人愿意在嵌入式场景测试这个?三天后,一个STM32微控制器的项目报告了首个生产环境用例——日志声明在bootloader里,实现烧录在应用固件中,正好绕过Rust的crate DAG限制。
这个用例从未出现在原始RFC的动机章节里。是社区聊天里的随口一提,让设计找到了真正的归宿。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.