Rust 包名的抢注困局,DNS 命名空间可能是最务实的解法


你在 crates.io 上发过 Rust 包吗?
如果发过,你大概率经历��一个让人抓狂的环节——取名字。你想叫 http,被占了。退一步叫 http-client,也被占了。再退一步叫 http-client-v2,还是被占了。最后你无奈地起了个 hyper-http-client-lite,然后看着这个名字陷入了沉思。Rust 社区最近有个 pre-RFC 提案想用 DNS 域名来解决这个问题。
crates.io 的命名困局
crates.io 采用的是扁平命名空间。全世界的 Rust 开发者共享一个"名字池",先到先得,跟当年抢 .com 域名一个逻辑。
这个设计在 Rust 生态早期没什么问题。那时候开发者少,好名字随便挑,单字 crate 满地跑。serde、tokio、rayon,又短又好记。
但 Rust 已经不是小众语言了。 crates.io 上现在有超过 15 万个包,每月新增约 2000 个,每天都有新包注册。问题开始暴露:
wasm、oauth、parser、metrics 这些词,早就名花有主。好名字被占完了。
更气人的是,有些包注册了好几年,代码就一个空壳,连 0.1 版本都没发过。名字被锁死了,别人想用也用不了。
开发者被迫在好名字前面加前缀、后面加后缀、中间加连字符,结果就是 cool-http-client-async-v2-tokio-edition 这种谁都记不住的东西。
npm 踩过的坑,Rust 正在踩
这个问题不是 Rust 独有的,任何包管理器生态膨胀到一定程度都会撞上同一堵墙。
Node.js 的 npm 早期也是扁平命名空间,结果更惨。因为 JavaScript 生态爆发得早、规模大,npm 上的包名战争比 crates.io 激烈得多。2015 年有个著名事件:一个开发者把左撤键盘的 left-pad 包从 npm 撤下来,直接导致成千上万个项目构建失败。
npm 后来引入了 @scope/package 的命名空间机制——@angular/core、@babel/parser,用组织名作为前缀,解决了大部分冲突。
Go 语言从设计之初就用了域名作为包路径。github.com/gin-gonic/gin,包名直接带上代码托管地址,从根本上回避了这个问题。Java 用反向域名(com.google.gson),也运行了二十多年。
Rust 的 crates.io 到现在还是全扁平的,这在 2026 年显得有点格格不入。
DNS 命名空间:用你自己的域名当前缀
最近 Rust 社区出现了一个 pre-RFC 提案,思路很直接:
你拥有一个域名,就能在 crates.io 上拥有一个命名空间。
举个例子。假设你拥有 mycompany.com,你可以发布这样的 crate:
mycompany.com/auth-sdk
mycompany.com/config-loader
mycompany.com/logger
域名就是命名空间的前缀,斜杠后面才是你自由发挥的空间。
个人开发者也没问题。你有一个 yourname.github.io,那就用它当命名空间:
yourname.github.io/base64
yourname.github.io/awesome-tool
这就像一个城市从"只有一条街、每家只能有一个名字"升级到了"有街道名和门牌号"的系统。你在长安街上开面馆和别人在南京路上开面馆,互不冲突。
Cargo 的处理方式:编译器无感知
这个提案里有一个很聪明的设计细节。
Cargo 在把 crate 名字传给 rustc 编译器之前,会先把域名前缀剥掉。也就是说 mycompany.com/base64 到了编译器眼里就是 base64。
rustc 不需要理解什么是域名、什么是命名空间,它看到的还是普通的 crate 名。改动也局限在 crates.io 注册和 Cargo 依赖解析的层面,不用动语言本身。如果项目同时依赖了全局的 base64 和 mycompany.com/base64,Cargo 现有的重命名机制也能处理。
用装修打个比方:域名前缀像是楼栋号,只在快递员(Cargo)配送时用到。等你进了屋子(编译器),就是普通的"厨房"、“卧室”,跟在哪个楼栋没关系。
.well-known 验证:域名所有权怎么确认
提案里域名所有权的验证方案也值得一提。不是什么复杂的 DNS TXT 记录或者区块链签名,就是在你的域名下放一个 JSON 文件:
https://mycompany.com/.well-known/rust-lang.org/package-namespace.json
这个文件里写着谁有权限在这个命名空间下发布包。crates.io 在你第一次注册这个命名空间时,会去验证这个文件。
选 .well-known URL 而不是 DNS 记录有个实际好处:你不需要真的拥有一个独立域名。一个 username.github.io 的 GitHub Pages 就够了——只要你能往上面放文件,就能通过验证。
这降低了个人开发者的使用门槛。不是只有公司才能用命名空间,个人开发者也可以。
方案故意留了很多事没做
这个提案最让我欣赏的地方是它的克制。
它没有试图禁止别人在扁平命名空间里注册 cool-wasm,哪怕你已经拥有 cool.com/wasm。它没有承诺解决所有命名纠纷。它甚至没有要求所有人必须用域名前缀。
它就做了一件事:给你一个可选的、可验证的、有结构的方式来组织你的包。
这种克制在基础设施提案里很少见。大多数类似提案的死因都是"想太多"——既要解决命名冲突,又要治理生态质量,还要处理安全信任链,最后摊子铺得太大,哪件事都做不好。
社区的审美之争
当然,反对的声音也有。
一部分 Rust 开发者觉得域名前缀太难看了。mycompany.com/base64 读起来不如单纯的 base64 优雅。有人担心这会让 Rust 的包管理变得像 Java 一样"企业味"十足。
这种审美偏好是可以理解的。Rust 社区一直有追求简洁和优雅的传统,扁平命名空间在早期确实很好用。
但生态规模变了,“好用"的定义也得跟着变。当一个注册了三年的空壳包堵住了你想要的 crate 名字,优雅不优雅就没那么重要了。
npm 早年也有人抱怨 @scope/package 的写法不够简洁,但现在没人想回到全扁平的时代。
Rust 开发者该关注什么
如果你是 Rust 开发者,这个提案对你的影响取决于你的场景:
你在发布自己的 crate 的话,域名命名空间给了你一条绕开命名冲突的路。先看看自己有什么域名(GitHub Pages 也算),再决定要不要用它当命名空间。
做企业级 Rust 项目的团队几乎是纯收益——用公司域名做前缀,既避免冲突又能建立辨识度。
只是使用别人的 crate 的话,短期内对你没什么影响。提案真落了地,Cargo.toml 里可能多几个域名前缀的依赖项,用法不变。
如果你想参与讨论,现在正是好时候。pre-RFC 阶段社区在征集反馈,对包管理有想法的开发者可以趁这个窗口发声。
觉得这篇对你有用?关注公众号「全栈之巅-梦兽编程」,每周更新 Rust 和 AI 编程实战内容。
也看看 梦兽编程 AI 编程助手服务 ,帮你把 AI 编程工具用到真正的生产环境里。
FAQ
这个提案已经被官方采纳了吗?
还没有。目前是 pre-RFC 阶段,也就是"提案前的提案”,主要用于收集社区反馈。距离正式进入 RFC 流程、最终落地还需要不少时间。但已经有人写出了可运行的 proof of concept。
用了域名前缀之后,Cargo.toml 怎么写?
大致是这样的格式:
[dependencies]
base64 = "0.22" # 全局命名空间的 base64
"mycompany.com/base64" = "1.0" # 域名命名空间的 base64
如果两个同时出现在一个项目里,可以用 Cargo 的 package 重命名机制来区分。
只有 .com 域名才能用吗?
不是。任何你能证明所有权的域名都行,包括 .io、.dev、.cn、.rs,甚至 username.github.io。验证方式是通过 .well-known URL,跟域名的顶级域没关系。
