首页
/ Rustlings 贡献指南:从提 Issue 到落地一道新练习的完整实操路径

Rustlings 贡献指南:从提 Issue 到落地一道新练习的完整实操路径

2026-09-03 15:55:37作者:范靓好Udolf

本文基于 Rustlings 仓库的 CONTRIBUTING.md 展开,系统讲解该项目的贡献规则、Issue 与 Pull Request 流程,以及新增练习的完整操作步骤:从文件命名、info.toml 元数据编写,到 dev check 自动校验与 CI 准入条件。读完后,你可以独立完成从报告 Bug 到提交并合并一道全新 Rust 练习的整个贡献闭环,并清楚每一道官方质量闸门背后由哪些源码逻辑把关。

贡献总原则:先沟通,再动手

Rustlings 对贡献者最核心的一条要求写在 CONTRIBUTING.md 的 Pull Requests 一节中:除非你的改动非常小且显而易见(small and trivial),请先开一个 Issue 讨论你的想法,再提交 Pull Request。仓库方鼓励贡献者耐心等待评审,也欢迎在遇到 Git 相关问题时直接求助。

文档中给出了一个按意图分流的快速参考表,贡献前可先对号入座:

我想做…… 推荐路径
报告一个 Bug 打开一个 Issue(见下文 Issues 一节)
修复一个 Bug 直接打开 Pull Request
实现一个新功能 先开 Issue 讨论,再开 Pull Request
添加一道新练习 按“新增练习”流程操作(见下文核心章节)
更新一道过时练习 打开 Pull Request,并同步检查对应答案

其中最后一条值得特别留意:更新一道练习时,必须检查它的答案(solution)是否也需要同步更新。这个要求不是口头约定,而是由仓库的自动化检查强制执行——dev check --require-solutions 会实际编译并运行 solutions/ 目录下的每个答案文件,详见“校验闸门”一节

LLM 使用政策

CONTRIBUTING.md 明确列出了贡献过程中使用 LLM(大语言模型)的三条边界:

  • 允许:个人使用,例如调研、辅助调试;
  • ⚠️ 必须披露:完全或部分由 LLM 生成的代码贡献需要主动说明,维护者对这类贡献的评审意愿可能更低;
  • 禁止:用 LLM 生成评论、Issue 或 PR 描述。原文的理由是:“我们想和你本人对话,而不是和你的 LLM 对话。”

该政策参照了 Rust 官方团队(rust-lang)发布的 LLM Usage Policy,体现了仓库对人机协作贡献的审慎态度:工具可以辅助,但沟通主体必须是贡献者本人。

报告 Bug:Issue 里必须包含什么

报告 Bug 时,CONTRIBUTING.md 要求 Issue 中附上以下命令的输出与环境信息:

- cargo --version
- rustlings --version
- ls -la
- 操作系统的名称和版本

这四条信息分别对应复现所需的关键维度:Cargo 工具链版本、Rustlings 程序版本、工作目录的实际内容(比如是否误在错误的目录运行了 rustlings)、以及操作系统。

为什么操作系统维度不可省略?从 CI 工作流 可以看到,cargo test --workspace 是在 ubuntu-latest、windows-latest、macos-latest 三个平台上按矩阵并行运行的,说明仓库把跨平台行为视为一等公民。贡献者在三个平台上的命令输出、文件权限行为可能存在差异,提供 OS 信息能帮助维护者直接缩小排查范围。

Pull Request:fork、提交与评审节奏

文档说明了 PR 的基本流程:fork 仓库、提交改动。对于练习类改动,有一条容易遗漏的配套动作——练习与其答案必须保持一致

  • 练习位于 exercises/<目录>/<练习名>.rs
  • 对应答案位于 solutions/<目录>/<练习名>.rs

例如修改 exercises/01_variables/variables1.rs 时,就要同步检查 solutions/01_variables/variables1.rs。如果 PR 只改了练习而答案仍然能通过全部检查,dev check 的“答案必须可运行”这一关就会替你兜底;反之如果改练习时把答案改坏了,PR 在 CI 阶段就会直接失败。

CI 工作流 可以确认,每个 PR 会依次经过:cargo clippy -- --deny warningscargo fmt --all --check、三平台 cargo test --workspacecargo dev check --require-solutions,以及 Markdown 风格检查(rumdl)。也就是说,你本地跑一遍 dev check 通过,基本就覆盖了 CI 的绝大部分准入条件——这一点在后文会展开。

新增一道练习:完整操作步骤

这是 CONTRIBUTING.md 中篇幅最大、也最实用的部分。新增练习的五个步骤依次为:命名规范 → 编写练习文件 → 编写 README → 编写答案 → 注册元数据,最后提交 PR。下面逐一展开,并结合源码说明每一步的“硬性依据”。

第一步:文件命名

  • 练习文件命名为 exercises/yourTopic/yourTopicN.rs,例如 exercises/yourTopic/yourTopic1.rs
  • exercises/yourTopic/README.md 中放一些有用的链接,并链接到 The Book(Rust 官方《The Rust Programming Language》书籍)的相关章节;
  • 在练习代码中,在需要用户修改的位置添加 // TODO: … 注释。

README 的作用是承接 The Book 之外的延伸阅读。可参考现有练习目录的写法,如 exercises/00_intro/README.md:先一句话说明本节主题(print!println! 宏),再在 “Further information” 小节列出参考链接。

// TODO 注释不是格式偏好,而是强制要求dev check 会逐字扫描每个练习文件,找不到 // TODO 就直接报错 “You need to have at least one such comment to guide the user”。其源码逻辑位于 src/dev/check.rscheck_info_file_exercises 函数中。

第二步:编写答案

  • solutions/yourTopic/yourTopicN.rs 添加答案文件,并用注释解释做法。

答案文件的路径规则与练习严格镜像:练习是 exercises/<dir>/<name>.rs,答案就是 solutions/<dir>/<name>.rs。这一对应关系在源码中由 src/exercise.rssol_path() 方法确定——它根据元数据里的 dirname 拼出答案路径,dir 缺省时退化为 solutions/<name>.rs

第三步:在 info.toml 中注册元数据

rustlings-macros/info.toml 文件中为练习添加元数据(官方练习集合的元数据统一维护在这个文件中),格式如下:

[[exercises]]
name = "yourTopicN"
dir = "yourTopic"
hint = """
A useful (multi-line) hint for your exercise.
Include links to a section in The Book or a documentation page."""

如果练习不含任何测试,就在元数据中加 test = false;但文档同时强调推荐添加测试

元数据字段的完整语义

对照 src/info_file.rsExerciseInfo 结构体的反序列化定义,每个字段的默认值和含义如下:

字段 是否必填 默认值 含义
name 练习名,即去掉 .rs 后缀的文件名
dir 练习在 exercises/ 下的目录名;缺省时练习路径为 exercises/<name>.rs
test true 是否运行 cargo test;置 false 后练习编译通过即视为完成
strict_clippy false 是否要求 Clippy 零警告才算通过
hint 用户输入 h 时显示的多行提示
skip_check_unsolved false 跳过“练习未解决”检查(供本来就是解决状态的介绍性练习使用)

几个关键细节:

  • 练习路径由 name + dir 拼出ExerciseInfo::path()src/info_file.rs)在有 dir 时生成 exercises/<dir>/<name>.rs,无 dir 时生成 exercises/<name>.rs。所以元数据里的 name 必须与磁盘上的文件名完全一致,否则 dev check 打开文件时就会报 “Failed to open the file”。
  • test#[test] 必须自洽test = false 但文件里存在 #[test],或者 testtrue(默认)但文件里没有 #[test],两者都会让 dev check 失败。这条规则保证了“元数据声明的行为”和“文件实际内容”不会出现偏差。
  • skip_check_unsolved 的真实用途dev check 会实际运行每个练习以确认它“尚未被解决”(防止提交人把答案误写进练习文件)。像 intro1 这种本来就要求编译通过的介绍性练习,通过在元数据中加 skip_check_unsolved = true 来豁免该检查——可以在 rustlings-macros/info.toml 开头看到官方练习正是这样配置的。

官方练习与社区练习的元数据位置差异

CONTRIBUTING.md 要求官方练习的元数据加到 rustlings-macros/info.toml,这是因为官方练习集合在构建 Rustlings 二进制时被嵌入(见 src/embedded.rs 的引用与 src/info_file.rs 中的 EMBEDDED_FILES.info_file)。运行时,InfoFile::parse() 的行为是:当前目录存在本地 info.toml 就优先解析它,否则回退到内嵌的官方练习表

这个设计意味着社区练习仓库(通过 rustlings dev new <path> 脚手架生成,实现见 src/dev/new.rs)把元数据直接放在仓库根目录的 info.toml 中,而官方仓库则维护 rustlings-macros/info.toml 这一内嵌源——两条路径共用同一套 ExerciseInfo 结构与校验逻辑。当前官方练习表的规模可从 rustlings-macros/info.toml 直接确认:文件以 format_version = 1 开头,并包含 94 个 [[exercises]] 条目。

第四步:让 Cargo 识别新二进制

新增练习文件后,不要手工往 Cargo.tomlbin 列表里加条目——官方仓库的 Cargo.toml 头部就注明“bin 列表由 rustlings dev update 自动更新,勿手工编辑”。运行:

rustlings dev update

即可根据 info.toml 重新生成 bin 列表。从 src/dev/update.rs 的实现可以看到:该命令解析 InfoFile 后重写 Cargo.toml 的 bin 段;在以源码调试构建(debug build)开发 Rustlings 本体时,它改写的则是 dev/Cargo.toml,且练习路径前缀为 ../(因为 dev crate 位于仓库内一层子目录)。相应地,dev check 会比对 Cargo.toml 的 bin 段是否与 info.toml 推导结果一致,不一致就报错 “Run rustlings dev update to update it”——所以新增练习后忘记执行 dev update 是 PR 被 CI 打回的一个常见原因

第五步:本地跑通 dev check,再开 PR

提交 PR 前,在仓库根目录执行:

rustlings dev check --require-solutions

这正是 CI 工作流dev-check job 运行的命令(cargo dev check --require-solutions)。本地通过,PR 的准入检查基本稳了。

校验闸门:dev check 在背后强制了什么

理解 dev check 检查了什么,等于理解了一道新练习要满足的全部硬性标准。以下规则全部来自 src/dev/check.rs 的源码:

元数据层面

  • 练习总数不得超过 999(MAX_N_EXERCISES);
  • name 长度不超过 32 字符,且只允许字母、数字和下划线(forbidden_char 函数对 namedir 同样生效);
  • name 全局唯一,重复即报错;
  • hint 不允许为空——即使是“不需要提示”,也要用文字说明为什么不需要;
  • info.tomlformat_version 必须与程序当前支持的版本相等:偏小要求迁移格式,偏大则提示升级 Rustlings 程序。

练习文件层面

  • 每个练习文件必须包含 fn main()(哪怕是空函数),以避免语言服务器报错;
  • 必须至少有一处 // TODO 注释;
  • #[test] 的存在与否必须与 test 字段声明一致;
  • exercises/ 目录(含一层子目录)只允许出现 README.mdinfo.toml 中登记的 .rs 练习文件,不允许更深层嵌套——任何游离文件都会被 check_unexpected_files 揪出来。

运行行为层面

  • 并行运行所有练习(受 skip_check_unsolved 豁免的除外),任何一个已经处于“已解决”状态就报错,防止把答案泄漏进练习;
  • 并行运行所有答案文件,答案必须编译、测试、Clippy 与运行全部通过;
  • 对通过的答案执行 rustfmt --check --edition 2024,答案必须已格式化;
  • --require-solutions 开启时,缺少答案文件直接判失败(官方仓库 CI 正是以该模式运行,因此官方练习的答案是必配项)。

练习与答案的“通过”定义来自 src/exercise.rsRunnableExercise::run:依次执行 cargo build →(若 test 为真)cargo testcargo clippy --profile teststrict_clippy 为真时追加 -D warnings)→ 运行二进制并要求退出码为 0。--profile test 保证 #[cfg(test)] 代码也被 Clippy 检查——这也解释了为什么练习里写测试代码同样要满足 Clippy 标准。

仓库内还有一组用于验证这套检查体系自身的测试工程:tests/test_exercises/ 下刻意准备了 compilation_failure.rscompilation_success.rstest_failure.rstest_success.rs 和未登记的 not_in_info.rs 五类样本,供集成测试(tests/integration_tests.rs)断言各类失败模式能被正确识别。

CI 准入清单:你的 PR 会经过哪些检查

汇总 .github/workflows/rust.yml 的定义,一个涉及仓库代码的 push/PR 会触发以下作业:

  1. clippycargo clippy -- --deny warnings(任何警告都是失败);
  2. fmtcargo fmt --all --check
  3. testcargo test --workspace,在 ubuntu/windows/macos 三平台矩阵上运行;
  4. dev-checkcargo dev check --require-solutions,即上文完整拆解的那套练习校验;
  5. rumdl:Markdown 风格检查。

值得注意的是该工作流的 paths-ignore 配置:*.md 文件、website/ 目录的改动不会触发这条流水线。所以仅修订 CONTRIBUTING.md 这类文档本身只经过最轻量的检查,而任何涉及练习、源码或 Cargo.toml 的改动都会完整走一遍上述闸门。

贡献前自查清单

综合 CONTRIBUTING.md 与源码校验逻辑,新增一道练习前可对照以下清单:

  1. 练习文件 exercises/<topic>/<topicN>.rs 已创建,含 fn main() 与至少一处 // TODO
  2. 目录 README.md 已补充主题说明与 The Book 章节链接;
  3. 答案 solutions/<topic>/<topicN>.rs 已编写、含解释性注释,且已 rustfmt 格式化;
  4. info.toml 已按 name / dir / hint 注册练习(官方练习写入 rustlings-macros/info.toml,社区练习仓库写入根目录 info.toml);无测试的练习已显式声明 test = false
  5. 已运行 rustlings dev update 刷新 Cargo.toml 的 bin 列表;
  6. rustlings dev check --require-solutions 本地全部通过;
  7. 非琐碎改动已先开 Issue 讨论;LLM 参与生成的代码部分已在 PR 中如实披露;
  8. 若同时修改了现有练习,其对应答案已同步更新。

这套“文档约定 + 自动校验”的双层结构,是 Rustlings 在保持低贡献门槛(fork 即可提交)的同时,让 94 道官方练习长期保持一致格式与行为的关键机制。

登录后查看全文
热门项目推荐
相关项目推荐