Rust 编译器 rustc 编码规范与贡献指南:从./x fmt到 PR 审查的完整实践
【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust
本文以 rustc-dev-guide 的 conventions.md 为核心,系统讲解 rustc 代码库的编码约定、格式化工具链(
./x fmt、tidy 脚本)、正确性编码技巧、crates.io 依赖策略、PR 提交结构以及编译器特有命名约定。读完你将掌握为 rust-lang/rust 贡献代码时"怎么写、怎么验、怎么提交"的完整工作流,并了解这些规则在当前仓库中的具体落地实现。
一、整体脉络
rustc 是 Rust 语言的编译器本体,代码规模庞大、参与者众多,因此形成了一套既遵循 Rust 社区通用规范、又针对编译器开发场景量身定制的编码约定。这些约定主要覆盖四个维度:
- 格式(Formatting):如何统一代码风格,如何被机器自动检查;
- 正确性(Coding for correctness):如何写出不易出错、便于 review 的代码;
- 依赖(crates.io 使用):编译器可以使用哪些第三方 crate、如何新增;
- PR 结构与命名:如何组织提交让审查者轻松,以及编译器内部约定俗成的命名习惯。
文档原文见 src/doc/rustc-dev-guide/src/conventions.md,本文在其基础上结合仓库内 tidy 工具源码、rustfmt.toml 配置等实现细节展开。
二、格式化与 tidy 脚本
2.1 为什么不能直接cargo fmt
rustc 正在逐步向 Rust 标准代码风格 靠拢,但现阶段并不使用稳定版rustfmt,而是使用一个固定的(pinned)版本配合特殊配置运行。这意味着:
- 用
cargo fmt格式化本仓库可能产生与官方风格不同的结果,官方明确不推荐; - 正确做法是使用
./x fmt; - 建议在每次提交前运行
./x fmt,这能显著减少后续的合并冲突。
仓库根目录的 rustfmt.toml 展示了这份"特殊配置"的真实面貌:
style_edition = "2024" use_small_heuristics = "Max" merge_derives = false group_imports = "StdExternalCrate" imports_granularity = "Module" use_field_init_shorthand = true可以看到它启用了style_edition = "2024"(新版风格版本)、imports_granularity = "Module"等不同于稳定版默认值的选项,这正是文档中"特殊配置"的落地点。
2.2 tidy 脚本如何检查格式
格式化由tidy 脚本统一把关,它会在你执行./x test时自动运行,也可以单独用./x fmt --check运行(只检查不修改)。
注意:格式与测试套件
tests/目录下的大部分 Rust 源文件不参与格式化,原因包括:对空白敏感(如 snapshot 测试)、注释位置敏感等。具体哪些文件被排除,见 rustfmt.toml 的ignore列表,例如/tests/ui/、/tests/pretty/、/tests/debuginfo/、/tests/incremental/等测试目录都在其中。
如果你希望在编辑器里使用"保存时自动格式化",pinned 版本的rustfmt二进制位于build/<target>/stage0/bin/rustfmt——即由构建系统生成的 stage0 工具链自带,无需依赖本机安装的 rustfmt。
从源码看,tidy 的样式检查逻辑位于 src/tools/tidy/src/style.rs,它实际执行的检查远不止格式化本身,包括(见该文件check_file_style函数):
- 行长度超过 100 字符(
COLS: usize = 100); - 非 CSS 文件中的 tab 字符;
- 行尾空白(trailing whitespace);
- CR 字符(
\r); TODO注释与todo!()宏;- 注释中句点后双空格;
- 未解释的
```ignoredoctest; - 文件行数超过 3000 行(
LINES: usize = 3000)。
2.3 格式化 C++ 代码
rustc 编译器中含有少量 C++ 代码,用于与 LLVM 中尚无稳定 C API 的部分交互(集中在 compiler/rustc_llvm/llvm-wrapper 目录)。修改这些代码时使用:
./x test tidy --extra-checks cpp:fmt --bless该命令使用pinned 版本的clang-format,避免依赖本地环境安装的版本差异。从 src/tools/tidy/src/extra_checks/mod.rs 的check_cpp_fmt实现可见:默认会遍历compiler/rustc_llvm/llvm-wrapper下的.h/.cpp文件,按根目录.clang-format配置执行--dry-run --Werror检查,加上--bless后则以-i模式直接改写文件。
2.4 格式化与 lint Python 代码
rust-lang/rust 仓库包含大量 Python 代码(构建脚本、CI 脚本、文档工具等),统一由ruff工具负责格式化和 lint:
# 格式化 Python 代码 ./x test tidy --extra-checks py:fmt --bless # 运行 Python lint ./x test tidy --extra-checks py:lint同样使用 pinned 版本的ruff,避免依赖本地环境。从 extra_checks/mod.rs 的实现可以看到完整机制:
- tidy 会在
build/venv下创建虚拟环境(要求 Python ≥ 3.11,源码中MIN_PY_REV),并按 src/tools/tidy/config/requirements.txt 安装依赖; - ruff 的配置位于 src/tools/tidy/config/ruff.toml;
py:lint默认执行ruff check,带--bless时追加--fix自动修复;失败时会打印ruff check --diff的差异建议;py:fmt默认执行ruff format --check,带--bless时直接格式化。
三、文件级格式约定
3.1 版权声明(Copyright notice)
过去 rustc 文件以版权与许可证声明开头,现在新文件如果采用标准许可(MIT OR Apache-2.0),应省略该声明。仓库中若仍残留旧版权通知,欢迎提交 PR 移除。对应地,tidy 在 style.rs 中专门检查包含 "Rust Project Developers" 字样的旧式版权行并报错。
3.2 行长度
- 行最长100 字符,能控制在 80 以内更好;
- 某些场景(尤其是测试)确实需要超长行时,可在文件顶部添加豁免注释:
// ignore-tidy-linelength该指令与ignore-tidy-*系列指令由 tidy 的 directive 解析器统一处理(见 src/tools/tidy/src/style/directive.rs),整文件或单行均可豁免。
3.3 Tab 与空格
统一使用4 空格缩进,禁止 tab。
四、为正确性而编码
除了格式,文档还给出了一组提升代码质量、降低审查成本的编码建议,且多数建议在 tidy 中都有对应的自动化支撑。
4.1 优先穷尽匹配(Prefer exhaustive matches)
在match中使用_通配固然方便,但代价是:当枚举新增变体时,落入_分支的代码可能得不到正确处理。写代码前先自问:如果这个枚举新增一个变体,它应该走_分支,还是需要专门处理?除非答案非常确定是前者,否则优先写穷尽匹配。同样的原则适用于if let和while let——它们本质上只针对单一变体做测试。
4.2 用// TODO记录待办事项
在提交落地前,可以用// TODO注释标记需要回头处理的事:
fn do_something() { if something_else { unimplemented!(); // TODO write this } }tidy 会对// TODO注释报错(见 style.rs 中"TODO is used for tasks that should be done before merging a PR"的检查),所以这段代码在 TODO 被修复或移除前无法合入。这种机制也被用来在 PR 的某个提交中故意留下一个 bug,并声明由后续提交修复:
if foo { return true; // TODO wrong, but will be fixed in a later commit }如果只是想给代码库留一条说明性备注,请改用// FIXME(tidy 同样会检查并引导使用 FIXME 而非XXX)。
4.3 遵循周围代码的风格
复用已有的辅助函数(helpers),避免重复实现相同逻辑或重复校验——与现有代码保持一致,能显著降低审查者的认知负担。
4.4 避免重复的"真相来源"
试图在两个不同位置之间保持数据同步(duplicated sources of truth)是一种典型的代码坏味道(code smell),应尽量让数据只在一个地方维护,其他地方只读引用。
4.5 用类型强制不变量
在可行的情况下,让非法状态无法被表示(make invalid states unrepresentable),而不仅仅是在构造时做检查。例如用枚举/新类型替代布尔标志位,让编译器在类型层面就排除掉非法组合,而不是依赖运行时的assert。
4.6 写有用的注释
注释应解释**"为什么"**而非"是什么"(write comments that saywhy, notwhat)。对于不直观的 bug 修复,值得在注释里详细说明来龙去脉——这类信息是后人排查问题的关键线索。
4.7 保留现有行为
修改代码时要考虑平台差异(platform differences)和错误处理路径,主动查找覆盖这些边界情况的既有测试,确保改动不破坏已承诺的行为。
4.8 小步前进
以小步、可独立测试的方式推进工作,每次有意义的改动后都运行相关测试,这样一旦出错能第一时间定位到是哪一步引入的。
五、使用 crates.io 依赖
rustc 编译器支持引入部分来自 crates.io 的依赖,但并非随意添加。Rust Forge 制定了官方的新依赖审查政策(third-party crates vetting policy),并且tidy 工具维护着一张允许使用的 crate 清单(见 src/tools/tidy/src/deps.rs)。
要新增一个编译器尚未使用的依赖,必须先把该 crate 加入允许清单,否则 tidy 检查会失败。这是编译器供应链安全的重要防线,也意味着第三方依赖的引入是一个有明确流程、需要评审的决策,而非代码里的随手一行Cargo.toml。完整说明见 crates-io.md。
六、如何组织你的 PR
提交的组织方式直接影响审查体验,文档给出五条实战建议:
把"纯重构"独立成提交。例如重命名一个方法,应把该重命名连同所有调用点的改动放在单独一个提交里,避免与逻辑改动混在一起。
提交多一点通常更好。大型改动几乎总是应该拆成更小、可独立理解的步骤(atomic commits)。需要注意的反面情况是:如果某个提交先引入一种实现策略,后面又大幅改掉它(而非在它之上增量扩展),这种"来回折腾"会让人困惑。
保持每个提交都格式化。虽然只有 PR 的最终提交必须格式正确,但用./x fmt逐个提交单独格式化,既便于审查,也能减少噪音。
禁止 merge commits。除 bors(自动合入机器人)外,仓库历史不允许出现合并提交。遇到冲突应改用 rebase,例如:
git rebase --interactive rust-lang/main(假设你的 remote 名为rust-lang。)
单个提交不必都能编译。官方不要求每个中间提交都构建成功——只需保证能在PR 级别做二分(bisect)定位问题即可。当然,如果能让每个提交单独构建通过,那永远是加分项。
七、命名约定
除了标准 Rust 命名规范外,编译器内部还有一套特有约定:
cx是 "context" 的常见缩写,常作为后缀使用。最典型的例子是tcx——Typing Context(类型上下文)的通用变量名,它是编译器类型检查阶段的核心数据结构;'tcx被用作 Typing Context 的生存期(lifetime)名称,贯穿整个类型系统代码;- 由于
crate是关键字,当需要一个表示"与 crate 相关"的变量时,通常改写拼写为krate。
关于tcx与'tcx的详细语义,参见 ty.md。
八、实践小结
| 场景 | 推荐命令/做法 |
|---|---|
| 格式化 Rust 代码 | ./x fmt(每次提交前运行) |
| 只检查不修改格式 | ./x fmt --check |
| 格式化 C++ 代码 | ./x test tidy --extra-checks cpp:fmt --bless |
| 格式化 Python 代码 | ./x test tidy --extra-checks py:fmt --bless |
| lint Python 代码 | ./x test tidy --extra-checks py:lint |
| 编辑器 format-on-save | 使用build/<target>/stage0/bin/rustfmt |
| 行长度 | ≤ 100(理想 ≤ 80),豁免用// ignore-tidy-linelength |
| 缩进 | 4 空格,禁 tab |
| 新文件版权声明 | 标准许可(MIT OR Apache-2.0)下省略 |
| 待办标记 | 合入前待办用// TODO(会被 tidy 拦截),代码库备注用// FIXME |
| 新增 crates.io 依赖 | 需先加入 src/tools/tidy/src/deps.rs 允许清单 |
| PR 提交 | 纯重构独立提交、每提交格式化、禁止 merge commits、冲突用 rebase |
以上约定在仓库中均有对应实现可查:格式配置见 rustfmt.toml,tidy 样式检查见 src/tools/tidy/src/style.rs,Python/C++ 等额外检查见 src/tools/tidy/src/extra_checks/mod.rs。遵循这些约定,既是让 PR 顺利通过 CI 的前提,也是让 rustc 这个超大代码库在数十年演进中保持可维护性的基础。
【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考