/// 与 //! 的渲染差异?
解读
在国内 Rust 社招/校招面试里,文档注释(doc comment)是高频考点。面试官问“渲染差异”并不是想听你背语法,而是想确认三件事:
- 你是否知道两种注释的作用域不同——一个给“下一项”写文档,一个给“当前容器”写文档;
- 你是否亲手用过
cargo doc --open
看过最终 HTML,能说出模块页与项页在导航栏、段落层级上的区别; - 你是否理解这种差异对公共 API 文档体验的影响,能否举例说明什么时候必须用
//!
才能避免“文档孤儿”。
知识点
- Outer doc comment
///
:附加在紧随其后的**条目(item)**上,渲染为该项的文档。 - Inner doc comment
//!
:附加在**当前容器(模块、crate、enum、struct、fn 等)**上,渲染为容器自身的文档。 - 容器首页:根模块(lib.rs/main.rs)只能用
//!
写 crate 级概述,否则文档首页空白。 - 模块级说明:子模块(mod.rs 或 foo.rs)若用
///
会错挂到“mod 关键字”后面的兄弟项,必须用//!
才能把文字收进模块页。 - 渲染路径差异:
–///
生成的 HTML 位于target/doc/crate_name/item_name/index.html
的“Structs/Enums/Functions”子栏;
–//!
生成的 HTML 位于同级index.html
的“Module”主栏,并出现在左侧导航的模块树节点上。 - 搜索索引:rustdoc 把
//!
内容全文收录进搜索索引,方便通过搜索框直达模块;///
仅收录对应项。 - 语法糖展开:
///
等价于#[doc = "..."]
放在下一项;//!
等价于#![doc = "..."]
放在当前作用域。 - 常见错误:在 lib.rs 顶部写
/// My crate...
会导致文档实际挂在“第一条 use 语句”上,首页依旧空白,CI 文档检查直接亮红灯。
答案
///
是外部文档注释,写给“下一个条目”,渲染成该项的文档;//!
是内部文档注释,写给“当前容器”,渲染成模块或 crate 自身的首页。
在 cargo doc
生成的 HTML 中:
///
出现在对应 struct/enum/fn 的独立子页,左侧导航归入“Structs/Enums/Functions”区域;//!
出现在模块索引页(index.html),位于左侧导航的模块树节点,并作为模块概述全文可搜索。
因此,lib.rs 与子模块文件顶部必须用//!
写 crate 或模块说明,否则文档首页或模块页会空白,造成公共 API 文档缺失。
拓展思考
- 混合场景:在同一个文件里先写
//!
给模块概述,再写///
给每个对外 API,既保证模块页有总览,又保证每个函数有独立示例,这是国内大厂开源 crate 的通行做法。 - CI 门禁:很多公司把
cargo doc --no-deps
的警告当错误处理,若误把///
写在文件头导致“文档未挂载”,流水线直接失败,面试时可举例自己如何通过#!![deny(missing_docs)]
捕获并修正。 - 宏导出:使用
#[doc(hidden)]
隐藏内部项时,若模块本身用//!
写的高层级指引仍能被搜索到,可显著降低新手误用内部 API 的概率,体现出你对文档驱动设计的理解。