Documentation Comments
Documentation comments (or doc comments) are special comments written in Markdown that describe public and private API items—such as functions, structs, enums, traits, modules, and crates. Unlike regular // comments, which the compiler discards, doc comments are parsed by the compiler into #[doc = "..."] attributes and used by rustdoc (cargo doc) to generate searchable HTML documentation and executable documentation tests (doctests).
Outer vs Inner Documentation Comments
Rust provides two kinds of doc comments, distinguished by whether they document the item following the comment or the item enclosing the comment:
| Syntax | Style | Name | What It Documents | Typical Placement |
|---|---|---|---|---|
/// ... |
Line | Outer doc comment | The item immediately following the comment | Directly above a fn, struct, enum, field, trait, or mod |
/** ... */ |
Block | Outer block doc comment | The item immediately following the comment | Directly above an item (less common than ///) |
//! ... |
Line | Inner doc comment | The crate, module, or block containing the comment | At the very top of src/lib.rs, src/main.rs, or inside mod name { //! ... } |
/*! ... */ |
Block | Inner block doc comment | The crate, module, or block containing the comment | At the top of a crate or module |
+-------------------------------------------------------------------+
| // src/lib.rs |
| //! Inner doc comment (`//!`): |
| //! Documents the enclosing crate or module as a whole. |
| |
| /// Outer doc comment (`///`): |
| /// Documents the `Rectangle` struct immediately below it. |
| pub struct Rectangle { |
| /// Outer doc comment on the `width` field. |
| pub width: u32, |
| /// Outer doc comment on the `height` field. |
| pub height: u32, |
| } |
+-------------------------------------------------------------------+
//! Utilities for basic temperature conversions.
/// Converts a temperature from Celsius to Fahrenheit.
///
/// Formula: `F = C * 9 / 5 + 32`
pub fn celsius_to_fahrenheit(celsius: f64) -> f64 {
celsius * 9.0 / 5.0 + 32.0
}
fn main() {
println!("0°C = {}°F", celsius_to_fahrenheit(0.0));
println!("100°C = {}°F", celsius_to_fahrenheit(100.0));
}
0°C = 32°F
100°C = 212°F
How the Compiler Desugars Doc Comments
To rustc, a doc comment is syntactic sugar for the #[doc = "..."] attribute. The following two definitions are identical to the compiler:
/// Returns the square of `x`.
///
/// Works for any `i32` that does not overflow.
pub fn square_comment(x: i32) -> i32 {
x * x
}
#[doc = " Returns the square of `x`."]
#[doc = ""]
#[doc = " Works for any `i32` that does not overflow."]
pub fn square_attribute(x: i32) -> i32 {
x * x
}
fn main() {
println!("{} and {}", square_comment(5), square_attribute(5));
}
25 and 25
Standard Markdown Structure and Sections
Doc comments support CommonMark Markdown—including bold/italic text, code spans, tables, lists, blockquotes, and intra-doc links ([Option]). By convention across the Rust standard library and ecosystem, item documentation follows a consistent layout:
- First line (Summary line): A single short sentence summarizing what the item does, written in third-person declarative style (
"Returns...","Creates...","Parses...").rustdocdisplays this first paragraph in module summary listings. - Detailed explanation: Additional paragraphs explaining behavior, algorithmic complexity, or corner cases.
- Standard
#headings:
| Heading | When to Include It |
|---|---|
# Examples |
Shows one or more runnable code blocks demonstrating how to call or construct the item |
# Panics |
Documents the exact conditions under which the function will call panic! (such as out-of-bounds indices or division by zero) |
# Errors |
If the function returns Result<T, E>, explains the conditions that cause an Err(E) to be returned |
# Safety |
If the function is pub unsafe fn, specifies the invariants the caller must uphold to avoid undefined behavior |
/// Divides `dividend` by `divisor`, returning the integer quotient.
///
/// # Panics
///
/// Panics if `divisor` is `0`.
///
/// # Examples
///
/// ```
/// let q = divide_exact(20, 4);
/// assert_eq!(q, 5);
/// ```
pub fn divide_exact(dividend: i32, divisor: i32) -> i32 {
assert!(divisor != 0, "divisor must not be zero");
dividend / divisor
}
fn main() {
println!("20 / 4 = {}", divide_exact(20, 4));
}
20 / 4 = 5
Executable Documentation Tests (Doctests)
When you run cargo test in a library crate (src/lib.rs), rustdoc extracts every Rust code block inside your doc comments, wraps it in a fn main() if none is present, compiles it against your crate, and executes it as an automated test. This guarantees that documentation examples never fall out of sync with the actual code.
Hiding Setup Lines in Doctests with #
Sometimes an example needs boilerplate setup lines (such as imports or helper definitions) to compile, but showing those lines would clutter the rendered documentation. Any line inside a doc-comment code block that starts with # (a hash followed by a space) is hidden in the generated HTML documentation while still compiled and run during cargo test:
/// Parses a positive port number from a string.
///
/// # Examples
///
/// ```
/// # fn main() -> Result<(), std::num::ParseIntError> {
/// let port: u16 = "8080".parse()?;
/// assert_eq!(port, 8080);
/// # Ok(())
/// # }
/// ```
pub fn is_privileged_port(port: u16) -> bool {
port < 1024
}
fn main() {
println!("80 is privileged: {}", is_privileged_port(80));
}
80 is privileged: true
In the rendered webpage produced by cargo doc, readers see only the two middle lines (let port: u16 = "8080".parse()?; and assert_eq!(port, 8080);), while cargo test compiles the full Result-returning main wrapper so the ? operator works.
Code Block Annotations for Doctests
| Code Fence Tag | cargo test Behavior |
|---|---|
``` or ```rust |
Compiles and runs the code block; test passes if it exits without panicking |
```should_panic |
Compiles and runs the code block; test passes only if execution panics |
```compile_fail |
Compiles the code block; test passes only if compilation fails |
```no_run |
Compiles the code block to verify syntax and types, but does not execute it (useful for network or file I/O examples) |
```ignore |
Completely skips compiling and running the block |
```text / ```bash |
Treated as non-Rust text; not compiled as a doctest |
Generating HTML Documentation
To build HTML documentation for your project and open it in your default browser:
cargo doc --open
To build documentation for your crate only (skipping dependencies) and include private items:
cargo doc --no-deps --document-private-items
Common Errors
Placing an Outer Doc Comment (///) with No Following Item
Because /// attaches a #[doc = "..."] attribute to whatever item follows it, leaving a /// comment at the very end of a block or file with no item after it fails to compile:
// ❌ does not compile
fn main() {
let x = 5;
println!("{x}");
/// Trailing outer doc comment with no item below it
}
error: expected item after doc comment
--> src/main.rs:4:5
|
4 | /// Trailing outer doc comment with no item below it
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this doc comment doesn't document anything
error: aborting due to 1 previous error
Placing an Inner Doc Comment (//!) After an Item
Inner doc comments (//!) that document a module or file must appear at the top of the module or file, before any items (fn, struct, use). Placing //! after an item inside a module is a syntax error unless you meant ///:
// ❌ does not compile
fn helper() {}
//! Misplaced inner doc comment after an item
fn main() {
helper();
}
error[E0753]: expected outer doc comment
--> src/main.rs:4:1
|
4 | //! Misplaced inner doc comment after an item
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: inner doc comments like this (starting with `//!` or `/*!`) can only appear before items
help: you might have meant to write a regular comment
|
4 - //! Misplaced inner doc comment after an item
4 + // Misplaced inner doc comment after an item
|
help: or an outer doc comment, if this was meant to document the following item
|
4 - //! Misplaced inner doc comment after an item
4 + /// Misplaced inner doc comment after an item
|
error: aborting due to 1 previous error