13.7Documentation comments
Lesson 1.2 covered ordinary comments, the // notes you leave for yourself and the next reader. Rust has a second, special kind of comment that does more: documentation comments become a browsable website, render formatted text, and can even run as tests. They're the foundation of Rust's documentation culture, which is one of the language's understated strengths, and writing them is a habit worth forming now.
/// documents the thing below it
A documentation comment uses three slashes, ///, and documents the item immediately following it, a function, struct, or module:
/// Returns the larger of two numbers.
///
/// If the two are equal, returns that value.
fn max(a: i32, b: i32) -> i32 {
if a >= b { a } else { b }
}
These look like ordinary comments but the compiler treats them specially: cargo doc collects every /// comment and builds an HTML documentation site from them, the same kind of site you browse for the standard library. The text supports Markdown, so the formatting you use in these lessons (headings, code, lists, links) all work in doc comments and render as styled HTML.
The convention is a one-line summary first, then a blank /// line, then any longer explanation. Tools show that first line in summary lists and the full text on the item's own page, so leading with a crisp sentence pays off.
Documentation sections and examples
Doc comments have a few conventional Markdown headings that readers expect. The most important is # Examples, holding a code block that shows the item in use:
/// Adds two numbers together.
///
/// # Examples
///
/// ```
/// let sum = my_crate::add(2, 3);
/// assert_eq!(sum, 5);
/// ```
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
Here's the part that surprises people: that example code is not just for show. cargo test actually compiles and runs the code blocks in your doc comments as tests, called doc tests (lesson 14.5 is devoted to them). If you change add so the example's assert_eq! would fail, the test suite catches it. Your documentation's examples can't drift out of sync with the code, because the moment they do, the build tells you. Other common sections include # Panics (when the function can panic, lesson 12.1) and # Errors (what a Result-returning function's Err means, lesson 12.2).
Key insight
Doc-test examples are documentation that cannot rot. The eternal problem with example code in docs is that it goes stale: the API changes, nobody updates the example, and now the docs actively lie. Rust runs your examples as part of the test suite, so a stale example is a failing build, not a silent lie. Documentation and code stay honest with each other automatically. This is a big reason Rust libraries tend to have trustworthy docs.
//! documents the enclosing item
There's a sibling form, //!, which documents the thing it's inside rather than the thing below it. You put it at the top of a file or module to describe that whole module or crate:
//! # My Tool
//!
//! A small library for counting words and lines.
//! Start with the [`run`] function.
/// Counts the words in a string.
pub fn run(text: &str) -> usize {
text.split_whitespace().count()
}
The //! block at the top of lib.rs becomes the front page of your crate's documentation, the overview a new user reads first. Use /// for "this item, described from outside" and //! for "the container I'm writing inside of, described from within." The two cover everything: per-item docs and crate/module overviews.
Building and reading the docs
cargo doc --open builds your crate's documentation (and its dependencies') and opens it in a browser. What you get is the same layout as the official standard-library docs and the per-crate pages on docs.rs, because they're all generated by the same tool from the same kind of comments. That uniformity is the point: every Rust crate's documentation looks and navigates the same way, so learning to read one means you can read them all, which is exactly what the next lesson is about.
Best practice
Document every public item with at least a one-line /// summary, and give non-trivial functions an # Examples block, both because it helps readers and because the example becomes a test. Private items need docs far less often; a plain // comment explaining a tricky internal is usually enough. The rule of thumb: anything pub is a promise to someone, so it deserves a sentence saying what the promise is.
Quiz time
Question #1
What's the difference between /// and //!?
Show solution
/// documents the item that immediately follows it (a function, struct, etc.). //! documents the item that encloses it, used at the top of a file or module to describe that whole module or crate. Use /// for per-item docs and //! for a crate or module overview.
Question #2
What happens to the code inside an # Examples block in a doc comment when you run cargo test?
Show solution
It's compiled and run as a test (a doc test). If the example fails to compile or an assertion in it fails, the test suite reports a failure. This keeps documentation examples from going stale: a broken example becomes a failing build rather than a silent lie. Lesson 14.5 covers doc tests in full.
Question #3
You're documenting a public pub fn divide(a: i32, b: i32) -> i32 that panics when b is 0. What doc-comment sections would you include?
Show solution
A one-line summary first, an # Examples block showing a normal call (which also becomes a doc test), and a # Panics section noting that it panics when b is 0. Documenting the panic condition is part of the function's contract (lesson 12.5): callers need to know the input that will crash it.
You can now write documentation that builds into a site and tests itself. The flip side of writing docs is reading them, and the next lesson, the last before the chapter summary, teaches a genuinely useful skill: how to read a method signature you've never seen on docs.rs and the standard-library docs.