name: write-documentation description: Write and format Rust documentation correctly. Apply proactively when writing code with rustdoc comments (//! or ///). Covers structure (inverted pyramid), intra-doc links (crate:: paths, reference-style), constant conventions (binary/byte literal/decimal), and formatting (cargo rustdoc-fmt). Also use retroactively via /fix-intradoc-links, /fix-comments, or /fix-md-tables commands.
Writing Good Rust Documentation
This consolidated skill covers all aspects of writing high-quality rustdoc:
- Structure - Inverted pyramid principle
- Links - Intra-doc link patterns
- Constants - Human-readable numeric literals
- Formatting - Markdown tables and cargo rustdoc-fmt
When to Use
Proactively (While Writing Code)
- Writing new code that includes
///or//!doc comments - Creating new modules, traits, structs, or functions
- Adding links to other types or modules in documentation
- Defining byte/u8 constants
Retroactively (Fixing Issues)
/fix-intradoc-links- Fix broken links, convert inline to reference-style/fix-comments- Fix constant conventions in doc comments/fix-md-tables- Fix markdown table formatting/docs- Full documentation check and fix
Part 1: Structure (Inverted Pyramid)
Structure documentation with high-level concepts at the top, details below:
╲────────────╱
╲ ╱ High-level concepts - Module/trait/struct documentation
╲────────╱
╲ ╱ Mid-level details - Method group documentation
╲────╱
╲ ╱ Low-level specifics - Individual method documentation
╲╱
Avoid making readers hunt through method docs for the big picture.
Placement Guidelines
| Level | What to Document | Example Style |
|---|---|---|
| Module/Trait | Why, when, conceptual examples, workflows, ASCII diagrams | Comprehensive |
| Method | How to call, exact types, parameters | Brief (IDE tooltips) |
Reference Up, Not Down
/// See the [module-level documentation] for complete usage examples.
///
/// [module-level documentation]: mod@crate::example
pub fn some_method(&self) -> Result<()> { /* ... */ }
Part 2: Intra-doc Links
Golden Rules
- Use
crate::paths (notsuper::) - absolute paths are stable - Use reference-style links - keep prose clean
- Place all link definitions at bottom of comment block
- Include
()for functions/methods - distinguishes from types
Link All Symbols for Refactoring Safety
Every codebase symbol in backticks must be a link. This isn't just style—it's safety.
When you rename, move, or delete a symbol:
- With links:
cargo docfails with a clear error pointing to the stale reference - Without links: The docs silently rot, referencing symbols that no longer exist
| Docs say | Symbol renamed to | With link | Without link |
|---|---|---|---|
[`Parser`] |
Tokenizer |
❌ Build error | ✅ Silently stale |
[`process()`] |
handle() |
❌ Build error | ✅ Silently stale |
Rule: If it's a symbol from your codebase and it's in backticks, make it a link.
// ❌ Bad: Will silently rot when Parser is renamed
/// Uses `Parser` for tokenization.
// ✅ Good: cargo doc will catch if Parser is renamed
/// Uses [`Parser`] for tokenization.
///
/// [`Parser`]: crate::Parser
Quick Reference
| Link To | Pattern |
|---|---|
| Struct | [Foo]: crate::Foo |
| Function | [process()]: crate::process |
| Method | [run()]: Self::run |
| Module | [parser]: mod@crate::parser |
| Section heading | [docs]: mod@crate::module#section-name |
| Dependency crate | [tokio::spawn()]: tokio::spawn |
✅ Good: Reference-Style Links
/// This struct uses [`Position`] to track cursor location.
///
/// The [`render()`] method updates the display.
///
/// [`Position`]: crate::Position
/// [`render()`]: Self::render
❌ Bad: Inline Links
/// This struct uses [`Position`](crate::Position) to track cursor location.
❌ Bad: No Links
/// This struct uses `Position` to track cursor location.
Linking to Dependency Crates
For crates listed in your Cargo.toml dependencies, use direct intra-doc links instead of
external hyperlinks to docs.rs. Rustdoc automatically resolves these when the dependency is built.
| Link To | Pattern |
|---|---|
| Crate root | [crossterm]: ::crossterm |
| Type in crate | [mio::Poll]: mio::Poll |
| Function in crate | [tokio::io::stdin()]: tokio::io::stdin |
| Macro in crate | [tokio::select!]: tokio::select |
✅ Good: Direct Dependency Links
//! **UI freezes** on terminal resize when using [`tokio::io::stdin()`].
//! Internally, cancelling a [`tokio::select!`] branch doesn't stop the read.
//! However, the use of [Tokio's stdin] caused the first two issues.
//!
//! [`tokio::select!`]: tokio::select
//! [`tokio::io::stdin()`]: tokio::io::stdin
//! [Tokio's stdin]: tokio::io::stdin
/// Uses [`mio::Poll`] to efficiently wait on file descriptor events.
///
/// [`mio::Poll`]: mio::Poll
//! Use [`crossterm`]'s `enable_raw_mode` for terminal input.
//!
//! [`crossterm`]: ::crossterm
❌ Bad: External docs.rs Links for Dependencies
/// Uses [mio::Poll](https://docs.rs/mio/latest/mio/struct.Poll.html) to wait.
Don't use docs.rs URLs for crates that are already in your Cargo.toml.
Why direct links are better for dependencies:
- Clickable in local
cargo docoutput (works offline) - Version-matched to your actual dependency version
- Validated by rustdoc (broken links caught at build time)
- Consistent style with internal crate links
✅ OK: External docs.rs Links for Non-Dependencies
For crates that are not in your Cargo.toml, external links are fine:
/// This is similar to how [rayon](https://docs.rs/rayon) handles parallel iteration.
Since rayon isn't a dependency, there's no local documentation to link to.
For detailed patterns, see link-patterns.md in this skill.
Part 3: Constant Conventions
Use human-readable numeric literals for byte constants:
| Type | Format | Example |
|---|---|---|
Bitmasks (used in &, |, ^) |
Binary | 0b0110_0000 |
| Printable ASCII | Byte literal | b'[' |
| Non-printable bytes | Decimal | 27 |
| Comments | Show hex | // (0x1B in hex) |
✅ Good: Human-Readable
/// ESC byte (0x1B in hex).
pub const ANSI_ESC: u8 = 27;
/// CSI bracket byte: `[` (91 decimal, 0x5B hex).
pub const ANSI_CSI_BRACKET: u8 = b'[';
/// Mask to convert control character to lowercase (0x60 in hex).
pub const CTRL_TO_LOWERCASE_MASK: u8 = 0b0110_0000;
❌ Bad: Hex Everywhere
pub const ANSI_ESC: u8 = 0x1B;
pub const ANSI_CSI_BRACKET: u8 = 0x5B;
pub const CTRL_TO_LOWERCASE_MASK: u8 = 0x60;
For detailed conventions, see constant-conventions.md in this skill.
Part 4: Formatting
Run cargo rustdoc-fmt
# Format specific file
cargo rustdoc-fmt path/to/file.rs
# Format all git-changed files
cargo rustdoc-fmt
# Format entire workspace
cargo rustdoc-fmt --workspace
What it does:
- Formats markdown tables with proper column alignment
- Converts inline links to reference-style
- Preserves code examples
If not installed:
cd build-infra && cargo install --path . --force
Verify Documentation Builds
cargo doc --no-deps
cargo test --doc
Code Examples in Docs
Golden Rule: Don't use ignore unless absolutely necessary.
| Scenario | Use |
|---|---|
| Example compiles and runs | ``` (default) |
| Compiles but shouldn't run | ```no_run |
| Can't make it compile | Link to real code instead |
| Macro syntax | ```ignore with HTML comment explaining why |
Linking to Test Modules and Functions
/// See [`test_example`] for actual usage.
///
/// [`test_example`]: crate::tests::test_example
Make test module visible to docs:
#[cfg(any(test, doc))]
pub mod tests;
Platform-Specific Test Modules
When you see this warning:
"unresolved link to
crate::path::test_module"And the module is
#[cfg(test)]only
Don't give up on links — Add conditional visibility instead of using plain text:
// Before (links won't resolve):
#[cfg(test)]
mod backend_tests;
// After (links resolve in docs):
#[cfg(any(test, doc))]
pub mod backend_tests;
// For platform-specific test modules:
#[cfg(all(any(test, doc), target_os = "linux"))]
pub mod linux_only_tests;
Apply at all levels — If linking to a nested test module, both parent and child modules need
the visibility change. See organize-modules skill for complete patterns.
Checklist
Before committing documentation:
- High-level concepts at module/trait level (inverted pyramid)
- All links use reference-style with
crate::paths - All link definitions at bottom of comment blocks
- Constants use binary/byte literal/decimal (not hex)
- Hex shown in comments for cross-reference
- Markdown tables formatted (
cargo rustdoc-fmt) - No broken links (
cargo doc --no-deps) - All code examples compile (
cargo test --doc)
Supporting Files
| File | Content | When to Read |
|---|---|---|
link-patterns.md |
15 detailed intra-doc link patterns | Writing links to modules, private types, test functions, section headings |
constant-conventions.md |
Full human-readable constants guide | Writing byte constants, decision guide |
examples.md |
5 production-quality doc examples | Need to see inverted pyramid in action |
rustdoc-formatting.md |
cargo rustdoc-fmt deep dive | Installing, troubleshooting formatter |
Related Commands
| Command | Purpose |
|---|---|
/docs |
Full documentation check (invokes this skill) |
/fix-intradoc-links |
Fix only link issues |
/fix-comments |
Fix only constant conventions |
/fix-md-tables |
Fix only markdown tables |
Related Skills
check-code-quality- Includes doc verification steporganize-modules- Re-export chains, conditional visibility for doc linksrun-clippy- May suggest doc improvements