|
| 1 | +--- |
| 2 | +name: m16-documentation |
| 3 | +description: "Use for writing Rust documentation comments. Keywords: 注释, comment, 文档, document, 添加注释, 写注释, add comment, doc comment, rustdoc" |
| 4 | +source: https://rust-coding-guidelines.github.io/rust-coding-guidelines-zh/ |
| 5 | +user-invocable: false |
| 6 | +--- |
| 7 | + |
| 8 | +# Rust 文档注释规范 |
| 9 | + |
| 10 | +## 快速参考 |
| 11 | + |
| 12 | +| 类型 | 符号 | 用途 | 必需性 | |
| 13 | +|------|------|------|--------| |
| 14 | +| 模块文档 | `//!` | crate 根、模块顶部 | 推荐 | |
| 15 | +| 项目文档 | `///` | 公开 API | **必需** | |
| 16 | +| 实现注释 | `//` | 复杂逻辑 | 按需 | |
| 17 | + |
| 18 | +## 核心规则 |
| 19 | + |
| 20 | +### 1. 公开 API 必须文档化 |
| 21 | + |
| 22 | +所有 `pub` 项目都需要 `///` 文档注释: |
| 23 | + |
| 24 | +```rust |
| 25 | +// ❌ 错误 |
| 26 | +pub fn process(data: &str) -> Result<()> { ... } |
| 27 | + |
| 28 | +// ✅ 正确 |
| 29 | +/// 处理输入数据并执行转换。 |
| 30 | +/// |
| 31 | +/// # Errors |
| 32 | +/// |
| 33 | +/// 数据格式无效时返回错误。 |
| 34 | +pub fn process(data: &str) -> Result<()> { ... } |
| 35 | +``` |
| 36 | + |
| 37 | +### 2. 文档注释结构模板 |
| 38 | + |
| 39 | +```rust |
| 40 | +/// 简短描述(一行,动词开头,以句号结尾)。 |
| 41 | +/// |
| 42 | +/// 详细描述段落(可选)。 |
| 43 | +/// 可以包含多行,解释功能、用途和注意事项。 |
| 44 | +/// |
| 45 | +/// # Arguments |
| 46 | +/// |
| 47 | +/// * `param` - 参数说明 |
| 48 | +/// |
| 49 | +/// # Returns |
| 50 | +/// |
| 51 | +/// 返回值说明。 |
| 52 | +/// |
| 53 | +/// # Errors |
| 54 | +/// |
| 55 | +/// 错误情况说明(Result 返回类型必需)。 |
| 56 | +/// |
| 57 | +/// # Example |
| 58 | +/// |
| 59 | +/// ``` |
| 60 | +/// use crate::module::Item; |
| 61 | +/// let item = Item::new(); |
| 62 | +/// ``` |
| 63 | +/// |
| 64 | +/// # Panics |
| 65 | +/// |
| 66 | +/// panic 条件(可能 panic 时必需)。 |
| 67 | +/// |
| 68 | +/// # Safety |
| 69 | +/// |
| 70 | +/// unsafe 代码的安全保证(unsafe 函数必需)。 |
| 71 | +``` |
| 72 | + |
| 73 | +### 3. 模块文档模板 |
| 74 | + |
| 75 | +```rust |
| 76 | +//! # 模块名称 |
| 77 | +//! |
| 78 | +//! 一句话描述模块功能。 |
| 79 | +//! |
| 80 | +//! ## 功能特性 |
| 81 | +//! |
| 82 | +//! - 特性 1:说明 |
| 83 | +//! - 特性 2:说明 |
| 84 | +//! |
| 85 | +//! ## 模块结构 |
| 86 | +//! |
| 87 | +//! - [`StructName`]: 结构说明 |
| 88 | +//! - [`function_name`]: 函数说明 |
| 89 | +``` |
| 90 | + |
| 91 | +### 4. 结构体文档模板 |
| 92 | + |
| 93 | +```rust |
| 94 | +/// 结构体的简短描述。 |
| 95 | +/// |
| 96 | +/// 详细说明用途和使用场景。 |
| 97 | +/// |
| 98 | +/// # Example |
| 99 | +/// |
| 100 | +/// ``` |
| 101 | +/// let item = Item::new(); |
| 102 | +/// ``` |
| 103 | +/// |
| 104 | +/// # Cloning |
| 105 | +/// |
| 106 | +/// (如适用)说明克隆行为和开销。 |
| 107 | +/// |
| 108 | +/// # Thread Safety |
| 109 | +/// |
| 110 | +/// (如适用)说明线程安全性。 |
| 111 | +pub struct Item { |
| 112 | + /// 字段说明(简短即可)。 |
| 113 | + pub field: String, |
| 114 | +} |
| 115 | +``` |
| 116 | + |
| 117 | +## 章节规则 |
| 118 | + |
| 119 | +### 必需章节 |
| 120 | + |
| 121 | +| 章节 | 使用条件 | |
| 122 | +|------|----------| |
| 123 | +| `# Arguments` | 函数有参数时 | |
| 124 | +| `# Returns` | 函数有返回值时 | |
| 125 | +| `# Errors` | 返回 `Result` 时 | |
| 126 | +| `# Panics` | 可能 panic 时 | |
| 127 | +| `# Safety` | `unsafe` 函数时 | |
| 128 | + |
| 129 | +### 可选章节 |
| 130 | + |
| 131 | +| 章节 | 使用场景 | |
| 132 | +|------|----------| |
| 133 | +| `# Example` | 公开 API 推荐 | |
| 134 | +| `# Cloning` | 实现了 `Clone` 且行为特殊 | |
| 135 | +| `# Thread Safety` | 并发相关 | |
| 136 | +| `# Performance` | 性能特征重要时 | |
| 137 | +| `# See Also` | 相关项目引用 | |
| 138 | + |
| 139 | +## 实现注释原则 |
| 140 | + |
| 141 | +### 何时需要实现注释 (`//`) |
| 142 | + |
| 143 | +1. **复杂逻辑**:非显而易见的算法 |
| 144 | +2. **性能优化**:为何选择这种实现 |
| 145 | +3. **边界情况**:特殊处理的条件 |
| 146 | +4. **锁作用域**:解释作用域限制 |
| 147 | +5. **TODO/FIXME**:待处理事项 |
| 148 | + |
| 149 | +```rust |
| 150 | +// 历史长度限制:保留最近 N 轮对话(2N 条消息) |
| 151 | +// 使用 split_off 高效截断,避免迭代器开销 |
| 152 | +if history.len() > self.max_history * 2 { |
| 153 | + *history = history.split_off(history.len() - self.max_history * 2); |
| 154 | +} |
| 155 | + |
| 156 | +// 添加用户消息到历史(使用独立作用域限制锁的生命周期) |
| 157 | +{ |
| 158 | + let mut conv = self.inner.conversation.write().await; |
| 159 | + conv.add_user_message(session_id, prompt); |
| 160 | +} |
| 161 | +``` |
| 162 | + |
| 163 | +### 何时不需要实现注释 |
| 164 | + |
| 165 | +```rust |
| 166 | +// ❌ 冗余:代码已自解释 |
| 167 | +let len = items.len(); // 获取长度 |
| 168 | + |
| 169 | +// ✅ 必要:解释原因 |
| 170 | +// 使用 Arc 包装,支持高效的克隆和共享 |
| 171 | +let shared = Arc::new(inner); |
| 172 | +``` |
| 173 | + |
| 174 | +## 注释写作规范 |
| 175 | + |
| 176 | +### 1. 解释 "为什么",不是 "是什么" |
| 177 | + |
| 178 | +```rust |
| 179 | +// ❌ 差:重复代码含义 |
| 180 | +// 遍历所有元素 |
| 181 | +for item in items.iter() { ... } |
| 182 | + |
| 183 | +// ✅ 好:解释意图和原因 |
| 184 | +// 使用迭代器而非索引,避免边界检查开销 |
| 185 | +for item in items.iter() { ... } |
| 186 | +``` |
| 187 | + |
| 188 | +### 2. 保持简洁 |
| 189 | + |
| 190 | +```rust |
| 191 | +// ❌ 冗长 |
| 192 | +/// 这个函数用于处理用户的输入数据,它会首先验证数据的格式是否正确, |
| 193 | +/// 然后将数据转换成内部格式,最后保存到数据库中。 |
| 194 | + |
| 195 | +// ✅ 简洁 |
| 196 | +/// 处理、验证并保存用户输入数据。 |
| 197 | +``` |
| 198 | + |
| 199 | +### 3. 使用完整句子 |
| 200 | + |
| 201 | +```rust |
| 202 | +// ❌ 不完整 |
| 203 | +/// returns the length |
| 204 | + |
| 205 | +// ✅ 完整 |
| 206 | +/// 返回集合中的元素数量。 |
| 207 | +``` |
| 208 | + |
| 209 | +### 4. 中英文混用规则 |
| 210 | + |
| 211 | +- 中文注释:团队成员都用中文 |
| 212 | +- 英文术语:保留原文(如 `Arc`, `Mutex`, `async`) |
| 213 | +- 代码示例:变量名用英文,注释用中文 |
| 214 | + |
| 215 | +```rust |
| 216 | +/// 使用 `Arc<Mutex<T>>` 实现共享状态。 |
| 217 | +/// |
| 218 | +/// 内部使用 `Arc` 包装,支持跨线程克隆共享。 |
| 219 | +``` |
| 220 | + |
| 221 | +## 示例代码规范 |
| 222 | + |
| 223 | +### 1. 示例代码格式 |
| 224 | + |
| 225 | +```rust |
| 226 | +/// # Example |
| 227 | +/// |
| 228 | +/// ``` |
| 229 | +/// use crate::module::Item; |
| 230 | +/// |
| 231 | +/// let item = Item::new(); |
| 232 | +/// let result = item.process("data"); |
| 233 | +/// ``` |
| 234 | + |
| 235 | +// 使用 `# ` 隐藏辅助代码 |
| 236 | +/// # Example |
| 237 | +/// |
| 238 | +/// ``` |
| 239 | +/// # use crate::module::Item; |
| 240 | +/// # let item = Item::new(); |
| 241 | +/// let result = item.process("data"); |
| 242 | +/// ``` |
| 243 | +``` |
| 244 | + |
| 245 | +### 2. 示例代码标记 |
| 246 | + |
| 247 | +| 标记 | 用途 | |
| 248 | +|------|------| |
| 249 | +| ```` ``` ```` | 默认:编译并运行测试 | |
| 250 | +| ```` ```no_run ```` | 编译但不运行(IO 操作等) | |
| 251 | +| ```` ```ignore ```` | 不编译也不运行(不完整示例) | |
| 252 | +| ```` ```compile_fail ```` | 应该编译失败(错误示例) | |
| 253 | + |
| 254 | +## 文档链接 |
| 255 | + |
| 256 | +### 1. 链接到其他项目 |
| 257 | + |
| 258 | +```rust |
| 259 | +/// 与 [`chat`](Self::chat) 类似,但支持流式输出。 |
| 260 | +/// 参考 [`ConversationManager`] 了解会话管理。 |
| 261 | +pub fn chat_stream(&self, ...) { ... } |
| 262 | +``` |
| 263 | + |
| 264 | +### 2. 链接语法 |
| 265 | + |
| 266 | +```rust |
| 267 | +[`Item`] // 同模块 |
| 268 | +[`module::Item`] // 其他模块 |
| 269 | +[`Item::method`] // 方法 |
| 270 | +[`method`](Item::method) // 显式路径 |
| 271 | +``` |
| 272 | + |
| 273 | +## 检查清单 |
| 274 | + |
| 275 | +### 写注释前检查 |
| 276 | + |
| 277 | +- [ ] 是否是公开 API?→ 必须有 `///` 文档 |
| 278 | +- [ ] 是否返回 `Result`?→ 必须有 `# Errors` |
| 279 | +- [ ] 是否可能 panic?→ 必须有 `# Panics` |
| 280 | +- [ ] 是否是 unsafe?→ 必须有 `# Safety` |
| 281 | +- [ ] 是否有参数?→ 应该有 `# Arguments` |
| 282 | +- [ ] 是否有返回值?→ 应该有 `# Returns` |
| 283 | + |
| 284 | +### 写注释时检查 |
| 285 | + |
| 286 | +- [ ] 第一行是否是简短描述? |
| 287 | +- [ ] 是否解释了 "为什么" 而非 "是什么"? |
| 288 | +- [ ] 示例代码是否可编译? |
| 289 | +- [ ] 注释是否与代码同步? |
| 290 | + |
| 291 | +### 写注释后检查 |
| 292 | + |
| 293 | +```bash |
| 294 | +# 生成文档并检查警告 |
| 295 | +cargo doc --no-deps |
| 296 | + |
| 297 | +# 运行文档测试 |
| 298 | +cargo test --doc |
| 299 | + |
| 300 | +# 使用 clippy 检查文档问题 |
| 301 | +cargo clippy -- -W clippy::doc_markdown |
| 302 | +``` |
| 303 | + |
| 304 | +## 常见错误 |
| 305 | + |
| 306 | +### 1. 缺少必需章节 |
| 307 | + |
| 308 | +```rust |
| 309 | +// ❌ 错误:返回 Result 但没有 # Errors |
| 310 | +pub fn load(path: &str) -> Result<Data> { ... } |
| 311 | + |
| 312 | +// ✅ 正确 |
| 313 | +/// 从文件加载数据。 |
| 314 | +/// |
| 315 | +/// # Errors |
| 316 | +/// |
| 317 | +/// 文件不存在或格式无效时返回错误。 |
| 318 | +pub fn load(path: &str) -> Result<Data> { ... } |
| 319 | +``` |
| 320 | + |
| 321 | +### 2. 示例代码不可编译 |
| 322 | + |
| 323 | +```rust |
| 324 | +// ❌ 错误:缺少导入 |
| 325 | +/// # Example |
| 326 | +/// |
| 327 | +/// ``` |
| 328 | +/// let item = Item::new(); // 未定义 Item |
| 329 | +/// ``` |
| 330 | + |
| 331 | +// ✅ 正确 |
| 332 | +/// # Example |
| 333 | +/// |
| 334 | +/// ``` |
| 335 | +/// use crate::module::Item; |
| 336 | +/// let item = Item::new(); |
| 337 | +/// ``` |
| 338 | +``` |
| 339 | + |
| 340 | +### 3. 注释与代码不同步 |
| 341 | + |
| 342 | +```rust |
| 343 | +// ❌ 错误:注释说返回 3,实际返回 4 |
| 344 | +/// 返回前 3 个元素。 |
| 345 | +fn top_three(items: &[i32]) -> &[i32] { |
| 346 | + &items[..4] // 实际返回 4 个 |
| 347 | +} |
| 348 | +``` |
| 349 | + |
| 350 | +## 工具支持 |
| 351 | + |
| 352 | +```bash |
| 353 | +# 检查文档缺失 |
| 354 | +cargo doc --open |
| 355 | + |
| 356 | +# 运行文档测试 |
| 357 | +cargo test --doc |
| 358 | + |
| 359 | +# clippy 文档检查 |
| 360 | +cargo clippy -- -W clippy::missing_docs_in_private_items |
| 361 | + |
| 362 | +# rustdoc 配置(lib.rs) |
| 363 | +#![warn(missing_docs)] |
| 364 | +``` |
| 365 | + |
| 366 | +## 自动检测缺失注释 |
| 367 | + |
| 368 | +### 使用 grep 检测 |
| 369 | + |
| 370 | +```bash |
| 371 | +# 检测缺少文档注释的 pub fn |
| 372 | +rg '^pub fn' -A 1 --type rust | rg '^[0-9]+:pub fn' -A 1 | rg -v '^[0-9]+-///' |
| 373 | + |
| 374 | +# 检测缺少文档注释的 pub struct |
| 375 | +rg '^pub struct' -A 1 --type rust | rg '^[0-9]+:pub struct' -A 1 | rg -v '^[0-9]+-///' |
| 376 | +``` |
| 377 | + |
| 378 | +### 使用 cargo missing_docs |
| 379 | + |
| 380 | +```bash |
| 381 | +# 安装 |
| 382 | +cargo install cargo-missing-docs |
| 383 | + |
| 384 | +# 运行检查 |
| 385 | +cargo missing-docs |
| 386 | +``` |
| 387 | + |
| 388 | +### CI 配置 |
| 389 | + |
| 390 | +```yaml |
| 391 | +# .github/workflows/docs.yml |
| 392 | +- name: Check documentation |
| 393 | + run: | |
| 394 | + cargo doc --no-deps |
| 395 | + cargo test --doc |
| 396 | + cargo clippy -- -W clippy::missing_docs_in_private_items |
| 397 | +``` |
0 commit comments