- 文档
【免费下载链接】rfcs
RFCs for changes to Rust
本指南系统讲解 Rust 项目(含 rustc 自身与标准库)的 API 文档注释(doc comment)编写规范,其源头是 2014 年提出的 RFC 505 及 2016 年修订它的 RFC 1574。读完本文,你将掌握从注释语法选择、摘要句撰写、Markdown 排版,到类型引用、文档链接、模块级与类型级文档分工的完整约定,并能直接套用到自己 crate 的 rustdoc 输出中。
规范从何而来:RFC 505 的定位与演进
RFC 505 是一份"约定型 RFC"(conventions RFC),发布于 2014-12-08,其核心使命是:为 Rust 项目(包括 Rust 语言本身)提供编写 API 文档的指导。它的动机非常直白:
Documentation is an extremely important part of any project. It's important that we have consistency in our documentation.
即文档是任何项目的重要组成部分,保持文档的一致性至关重要。值得注意的一点是,RFC 505 提出的大部分准则在当时已被社区日常遵循,这份 RFC 的价值在于为这些"约定俗成"的行为提供动机和澄清,把它们从隐性共识变成可引用的显式规范。
该 RFC 已被 RFC 1574 修订,后者在 Appendix A(合并版规范全文) 中给出了 RFC 505 与自身修改合并后的完整文本,本文后续内容即按该合并版展开。
适用范围:多数准则适用于任何 Rust 项目;但有一部分专门针对 rustc 自身和标准库的文档(如"English"一节),原文已在正文中单独标注。规划规范时原文还列出了一份标准的小节清单(Examples / Panics / Failure),这在合并版中被更新与扩充,详见下文"Markdown 用法"。
注释形式:用行注释代替块注释
第一条约定是注释形式层面的:避免块注释,优先使用行注释。例如描述一段行为时,RFC 推荐:
// Wait for the main task to return, and set the process error code // appropriately.而不是:
/* * Wait for the main task to return, and set the process error code * appropriately. */其次是内部文档注释(inner doc comment)的使用边界://!只能用于撰写 crate 级和 module 级文档,除此之外不要使用。当要注释一个mod块时,应优先把///写在块外面:
/// This module contains tests mod tests { // ... }而不是写在块内部:
mod tests { //! This module contains tests // ... }这条约定的实践价值在于://!的语义是把文档"注入"到当前容器(crate/module)内部,因此它天然只适合模块与 crate 概览;而///用于具体条目(函数、结构体、trait 等),两者分工明确后,读者看到//!就能立刻预期后面跟着的是整体概览,看到///则预期是某个 API 条目的说明。
摘要句与整体格式:第一行决定 rustdoc 的印象分
任何 doc comment 的第一行应当是一句单行短句,对该代码给出摘要(summary)。这一行会被 rustdoc 在索引页、侧边栏、搜索结果等处反复用作摘要描述,因此保持简短非常划算。紧接着的格式要求有三条:
- 标点完备:所有 doc comment(包括摘要行)都应使用正确的标点符号;优先写完整句子,而不是碎片化短语。
- 人称与时态固定:摘要行应使用第三人称单数现在时陈述式(third person singular present indicative)。说白了,写 "Returns" 而不是 "Return"。这条约定直接决定了 rustdoc 中大量标准库文档的第一行形态,例如本仓库 text/1123-str-split-at.md 中
str::split_at的注释第一行就是 "Divide one string slice into two at an index.",text/1328-global-panic-handler.md 中 panic API 的第一行则是 "Unregisters the current panic handler, returning it."、"Returns the payload associated with the panic." 等——都是第三人称现在时的完整句子。
在合并版规范中,"Summary sentence"一节(Appendix A)再次强调了这两点,并将其明确界定为**对 API 文档(而非普通注释)**的要求。
Markdown 用法:排版、代码块、小标题与引用风格链接
doc comment 内部允许使用 Markdown 排版,RFC 对用法给出了明确约束。
用一级标题划分小节
使用顶层标题#在注释内划分小节。RFC 505 列出的常见小节标题为:
- Examples
- Panics
- Failure
合并版(RFC 1574)将清单更新为:
- Examples
- Panics
- Errors
- Safety
- Aborts
- Undefined Behavior
一个重要细节:即使只包含一个示例,也使用复数形式 "Examples" 而不是 "Example"。理由是为了让未来的工具处理更一致(不必同时兼容单复数两种标题)。本仓库中大量标准库 API 提案都严格遵循了这一约定,例如 text/0574-drain-range.md 中Vec::drain的注释同时使用了 "Panics" 与 "Safety" 风格的小节组织,text/1123-str-split-at.md 中的split_at注释则依次包含 "Panics" 与 "Examples" 两节。
行内代码与多行代码块
- 用反引号(
`)标注句内的代码片段; - 用三反引号(
```)撰写较长的示例:
This code does something cool. ```rust let x = foo(); x.bar();### 代码块语言标注:rustdoc 会执行所有 Rust 示例 在合适的地方利用 rustdoc 的格式化指令,为三反引号代码块标注对应的语言:println!("Hello, world!");puts "Hello"RFC 505 主张尽量显式标注 `rust`,理由是让 GitHub 等不会默认按 Rust 高亮的平台也能正确着色。合并版则做了细化:**在 API 文档中可以依赖 rustdoc 的默认值(默认即为 rust)**,例如: ```rust /// For example: /// /// ``` /// let x = 5; /// ```但在长文档(long-form documentation)中仍应始终显式标注rust。无论哪种情况,一条硬性约束不变:rustdoc 会实际编译并运行文档内嵌入的所有 Rust 示例,因此凡是非 Rust 的代码块必须正确标注语言,否则你的文档测试(doctest)会失败。这正是"标注语言"从美观问题升格为正确性问题的根本原因。
引用风格链接优于内联链接
引用和引证应使用"引用风格"(reference style)链接。RFC 505 推荐的写法:
[Rust website][1] [1]: http://www.rust-lang.org优于:
[Rust website](http://www.rust-lang.org)合并版在此基础上进一步简化:优先省略第二个[](即 Markdown 的 shortcut reference link 形式):
[Rust website] [Rust website]: http://www.rust-lang.org只有当链接文字很长时,才允许使用带标签的完整形式:
This link [is very long and links to the Rust website][website]. [website]: http://www.rust-lang.org英文规范:标准库统一使用美式英语
"English"一节明确只适用于 rustc 和标准库的文档:所有文档在拼写、语法、标点惯例上统一采用美式英语(American English)。同时 RFC 也给出了一个务实的免责声明:语言是随时间演变的,因此并非每个语法问题都存在唯一正确答案,但通常存在某种正式共识(formal consensus)可供遵循。这条约定保证了标准库文档(以及 rustc 内部文档)在跨贡献者、跨时期协作时风格的一致性。
类型引用与文档链接:把"说清楚"落到实处
合并版新增了三组与措辞和链接有关的约定,它们直接提升文档的可读性与可导航性。
引用类型要用全名
当谈及某个类型时,使用其全名:泛型类型应写Option<T>,而不是Option。唯一例外是 trait bound:写Cow<'a, B>即可,而不用写完整的Cow<'a, B> where B: 'a + ToOwned + ?Sized。另一种可行策略是使用小写泛称词:例如 'string' 可以同时指String或&str,'an option' 可以指代Option<T>。
Link all the things:手动补齐类型链接
Markdown 的一个重大缺陷是不会在 API 文档中自动为类型生成链接。RFC 要求作者自己用引用风格语法补上,以方便阅读:
/// The [`String`] passed in lorum ipsum... /// /// [`String`]: ../string/struct.String.html这一痛点后来促成了 RFC 1946(intra-doc links) 的诞生。该 RFC 指出现有手工相对链接的三种脆弱性:同一 doc comment 会被渲染到多个目录层级(如模块摘要页与结构体自身页),相对路径无法在所有位置生效;绝对路径在cargo doc --open(file:///URL)和 docs.rs 环境下会失效;一旦 rustdoc 的文件命名方案变更(历史上确实发生过,见 Rust issue #35236),所有手工链接都需要更新。RFC 1946 的解决方案是让链接直接写 Rust item 路径,如Iterator,由 rustdoc 自动解析成可工作的链接——这是"Link all the things"约定在现代 rustdoc 中的官方演化形态。
模块级文档与类型级文档的分工
标准库长期存在模块级文档与类型级文档之间的张力。RFC 举例:当时各类*Cell类型的文档页都写着"refer to the module-level documentation for more details"(更多细节请参阅模块级文档),这迫使读者在多个页面间跳转才能获得完整信息。
正确的分工原则是:
- 模块级文档给出模块内所有内容的高层概览,应当宽泛、不过度深入;
- 每个类型完整地自文档化(document itself fully),细节留给类型自己的文档;
- 允许两者存在少量重复。
这样既保证了模块概览的导航价值,又让每个类型页面可独立阅读,不必"被迫"跳转。
完整示例:一个符合全部规范的 crate
RFC 1574 提供了一个完整的示例 crate(基于作者自己的 ref_slice crate 骨架,作者明确说明重点在文档而非代码质量),完整展示了上述约定如何落地。
在lib.rs中,crate 级文档使用//!描述整体功能、布局(Layout)小节以及类型链接:
//! Turning references into slices //! //! This crate contains several utility functions for taking various kinds //! of references and producing slices out of them. In this case, only full //! slices, not ranges for sub-slices. //! //! # Layout //! //! At the top level, we have functions for working with references, `&T`. //! There are two submodules for dealing with other types: `option`, for //! &[`Option<T>`], and `mut`, for `&mut T`. //! //! [`Option<T>`]: http://doc.rust-lang.org/std/option/enum.Option.html pub mod option; /// Converts a reference to `T` into a slice of length 1. /// /// This will not copy the data, only create the new slice. /// /// # Panics /// /// In this case, the code won't panic, but if it did, the circumstances /// in which it would would be included here. /// /// # Examples /// /// ``` /// extern crate ref_slice; /// use ref_slice::ref_slice; /// /// let x = &5; /// /// let slice = ref_slice(x); /// /// assert_eq!(&[5], slice); /// ``` /// /// A more complex example. In this case, it's the same example, because this /// is a pretty trivial function, but use your imagination. /// /// ``` /// extern crate ref_slice; /// use ref_slice::ref_slice; /// /// let x = &5; /// /// let slice = ref_slice(x); /// /// assert_eq!(&[5], slice); /// ``` pub fn ref_slice<T>(s: &T) -> &[T] { unimplemented!() }注意其中对# Panics的处理:作者特意演示了"该函数实际不会 panic,但若会 panic,触发条件应写在这里"的占位式写法,明确了该小节的标准语义。unsafe相关 API 则对应# Safety小节,注释中说明若代码需标记为 unsafe,读者应在此了解需要维护的不变量(invariants):
/// Functions that operate on mutable references. /// /// This submodule mirrors the parent module, but instead of dealing with `&T`, /// they're for `&mut T`. mod mut { /// Converts a reference to `&mut T` into a mutable slice of length 1. /// /// This will not copy the data, only create the new slice. /// /// # Safety /// /// In this case, the code doesn't need to be marked as unsafe, but if it /// did, the invariants you're expected to uphold would be documented here. /// /// # Examples /// /// ``` /// extern crate ref_slice; /// use ref_slice::mut; /// /// let x = &mut 5; /// /// let slice = mut::ref_slice(x); /// /// assert_eq!(&mut [5], slice); /// ``` pub fn ref_slice<T>(s: &mut T) -> &mut [T] { unimplemented!() } }在option.rs中,子模块文档同样以//!开头并链接到Option<T>,类型链接([Option]: ...)在每个需要的位置重复声明,因为引用风格链接的定义是文档局部的:
//! Functions that operate on references to [`Option<T>`]s. //! //! This submodule mirrors the parent module, but instead of dealing with `&T`, //! they're for `&`[`Option<T>`]. //! //! [`Option<T>`]: http://doc.rust-lang.org/std/option/enum.Option.html /// Converts a reference to `Option<T>` into a slice of length 0 or 1. /// /// [`Option<T>`]: http://doc.rust-lang.org/std/option/enum.Option.html /// /// This will not copy the data, only create the new slice. /// /// # Examples /// /// ``` /// extern crate ref_slice; /// use ref_slice::option; /// /// let x = &Some(5); /// /// let slice = option::ref_slice(x); /// /// assert_eq!(&[5], slice); /// ``` /// /// `None` will result in an empty slice: /// /// ``` /// extern crate ref_slice; /// use ref_slice::option; /// /// let x: &Option<i32> = &None; /// /// let slice = option::ref_slice(x); /// /// assert_eq!(&[], slice); /// ``` pub fn ref_slice<T>(opt: &Option<T>) -> &[T] { unimplemented!() }这个例子值得逐条对照前文规范:摘要句均为第三人称现在时完整句("Converts..."、"This will not copy the data...");小节使用复数 "Examples";多个示例之间用自然语言段落过渡("A more complex example."、"Nonewill result in an empty slice:");示例代码块默认由 rustdoc 编译测试;类型一律使用全名(Option<T>);模块概览(Layout)与类型细节各司其职。
仓库中的实践佐证:规范如何贯穿标准库提案
这份规范并非停留在纸面。本仓库中大量标准库 API 的 RFC 都在 doc comment 层面身体力行地遵循着这些约定,可作为现成的范例阅读:
- text/0574-drain-range.md 中
Vec::drain与String::drain的 API 注释:第一行是第三人称现在时摘要("Creates a draining iterator that clears the specified range..."),随后是 "Panics" 小节说明两种触发 panic 的具体条件(range 递减、上界超出长度、字符边界问题)。 - text/1123-str-split-at.md 中
str::split_at的注释:完整展示了"摘要句 + 补充说明 +# Panics+# Examples(含可运行的 doctest)"的标准四段式结构,示例中还演示了 Unicode 字符串切片这一边界场景。 - text/1328-global-panic-handler.md 中
std::panic模块的 API:每个函数都以 "Returns..."、"Registers..." 式第三人称现在时开头,并以# Panics小节说明嵌套 panic 会导致进程 abort 的极端语义。
这些例子说明:从 2014 年 RFC 505 确立规范起,标准库 API 提案就已经在实际使用这套约定撰写文档,并延续至今。
备选方案与遗留问题
RFC 505 在收尾部分给出了简洁的评估:
- Drawbacks(缺点):None(无)。
- Alternatives(备选方案):不制定文档规范(Not having documentation guidelines)。即唯一被考虑的替代方案就是放任自流,而 RFC 明确否定了它——这正是文档一致性需要规范的直接论证。
- Unresolved questions(未决问题):None(无)。
RFC 1574 则补充了一个诚实的自我审视:有可能 RFC 505 已经足够,而如此细致的补充规范并不必要("It's possible that RFC 505 went far enough, and something this detailed is inappropriate");其备选方案同样是"维持前一个 RFC 的最小约定"。最终结论仍然是:这套合并后的完整规范被采纳,成为 Rust 官方文档写作的事实标准。
总结
RFC 505 与 RFC 1574 共同构成了 Rust API 文档注释的官方规范体系,核心要点可归纳为六条:
- 形式:用行注释
//,//!仅用于 crate/module 级文档,mod块优先在块外用///; - 摘要句:第一行是第三人称现在时的短句,全文标点完备;
- Markdown:用
#划分小节(Examples/Panics/Errors/Safety/Aborts/Undefined Behavior),单数内容也用复数标题,代码块正确标注语言(因为 rustdoc 会测试所有 Rust 示例); - 链接:优先引用风格(shortcut reference)链接,并手动为类型补全链接(现代 rustdoc 已演进为 intra-doc links);
- 措辞:类型用全名(
Option<T>),标准库统一美式英语; - 分层:模块级文档做高层概览,类型文档自文档化,允许少量重复。
将这些约定落实到自己的 crate 中,既能保证 rustdoc 输出的一致性、可搜索性与可测试性,也让自己编写的 API 文档与 Rust 标准库保持同一套话语体系。规范全文可查阅 RFC 505 原文 与 RFC 1574 的合并版 Appendix A。
- 文档
【免费下载链接】rfcs
RFCs for changes to Rust
相关推荐
一级标题:文档主题(如"列表推导式教程")
一级标题:文档主题(如"列表推导式教程") 二级标题:主要章节(如"基础语法") 三级标题:子章节(如"条件过滤") 四级标题:细节说明(如"多条件组合") 最
示例工程Rust unsafe API 的落位与命名约定:RFC 0240 的 `raw` 子模块与 `_unchecked` 后缀规范
Rust unsafe API 的落位与命名约定:RFC 0240 的 raw 子模块与 _unchecked 后缀规范 导读 本文围绕 Rust RFC 02
文档HsMod代码注释规范:XML文档注释编写指南
HsMod代码注释规范:XML文档注释编写指南 在HsMod项目开发中,规范的代码注释是提升团队协作效率、降低维护成本的关键。本文将详细介绍XML文档注释的编写
游戏开发
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考