/// 与 //! 的渲染差异?

解读

在国内 Rust 社招/校招面试里,文档注释(doc comment)是高频考点。面试官问“渲染差异”并不是想听你背语法,而是想确认三件事:

  1. 你是否知道两种注释的作用域不同——一个给“下一项”写文档,一个给“当前容器”写文档;
  2. 你是否亲手用过 cargo doc --open 看过最终 HTML,能说出模块页项页在导航栏、段落层级上的区别;
  3. 你是否理解这种差异对公共 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 文档缺失。

拓展思考

  1. 混合场景:在同一个文件里先写 //! 给模块概述,再写 /// 给每个对外 API,既保证模块页有总览,又保证每个函数有独立示例,这是国内大厂开源 crate 的通行做法。
  2. CI 门禁:很多公司把 cargo doc --no-deps 的警告当错误处理,若误把 /// 写在文件头导致“文档未挂载”,流水线直接失败,面试时可举例自己如何通过 #!![deny(missing_docs)] 捕获并修正。
  3. 宏导出:使用 #[doc(hidden)] 隐藏内部项时,若模块本身用 //! 写的高层级指引仍能被搜索到,可显著降低新手误用内部 API 的概率,体现出你对文档驱动设计的理解。