Documentation
Rust's Documentation Culture
Documentation in programming languages often comes with two problems:
- Weak motivation to write docs: It requires extra work and must keep up with code changes
- Documentation drifts from code: Over time, docs become outdated and no longer match the code's actual behavior
Rust provides solutions to both problems at the language and toolchain level.
Doc Comments
Rust's doc comments start with /// (outside an item) or //! (inside an item):
/// Adds two numbers.
///
/// # Examples
///
/// ```
/// let result = my_crate::add(1, 2);
/// assert_eq!(result, 3);
/// ```
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
//! # My Crate
//!
//! `my_crate` is a library that provides mathematical operations.
//! This documentation serves as the crate-level description.
Doc comments are written in Markdown. Headings, lists, code blocks, links, tables -- all Markdown syntax is available.
Conventional Sections
Rust documentation has conventional sections:
/// Reads data from a TCP stream.
///
/// # Arguments
///
/// * `buf` - Buffer to store the data
///
/// # Returns
///
/// Returns the number of bytes read.
///
/// # Errors
///
/// Returns `io::Error` if the socket is closed.
///
/// # Panics
///
/// Panics if `buf` has length 0.
///
/// # Safety
///
/// (For unsafe functions) Describes the preconditions for safely calling this function.
///
/// # Examples
///
/// ```no_run
/// use std::net::TcpStream;
/// use std::io::Read;
///
/// let mut stream = TcpStream::connect("127.0.0.1:8080").unwrap();
/// let mut buf = [0u8; 1024];
/// let n = stream.read(&mut buf).unwrap();
/// ```
pub fn read(buf: &mut [u8]) -> io::Result<usize> {
// ...
}
| Section | Purpose |
|---|---|
# Examples |
Usage examples (most important) |
# Errors |
For functions returning Result, what errors can occur |
# Panics |
Conditions that may cause a panic |
# Safety |
Preconditions for unsafe function safety |
# Arguments |
Argument descriptions |
Doc Tests
The most innovative feature of Rust's documentation is doc tests.
Code blocks (enclosed in ```) within doc comments are automatically executed as tests when you run cargo test:
/// Determines whether a number is even.
///
/// # Examples
///
/// ```
/// assert!(my_crate::is_even(4));
/// assert!(!my_crate::is_even(3));
/// ```
pub fn is_even(n: i32) -> bool {
n % 2 == 0
}
cargo test
Doc-tests my_crate
running 1 test
test src/lib.rs - is_even (line 5) ... ok
test result: ok. 1 passed; 0 failed; 0 ignored
Why this is revolutionary:
- Documentation doesn't drift from code: Code examples in docs are actually compiled and run, so doc tests fail when the API changes
- Motivation to write docs emerges: Since code examples in doc comments become tests directly, writing tests and writing documentation become one and the same
- Sample code correctness is guaranteed: When users copy-paste code examples from documentation, they're guaranteed to work
Doc Test Options
/// # Examples
///
/// Only verify that compilation succeeds (don't run):
/// ```no_run
/// let mut file = std::fs::File::create("output.txt").unwrap();
/// ```
///
/// Expect a compilation error:
/// ```compile_fail
/// let x: i32 = "hello";
/// ```
///
/// Don't run as a test (pseudocode or explanatory purpose):
/// ```ignore
/// // This example requires an external service
/// let response = fetch_from_api().await;
/// ```
///
/// Hidden lines (hidden in doc display, executed in tests):
/// ```
/// # use std::collections::HashMap;
/// # fn main() {
/// let mut map = HashMap::new();
/// map.insert("key", "value");
/// assert_eq!(map["key"], "value");
/// # }
/// ```
Lines starting with # are hidden in the HTML doc display but included as code during test execution. This lets you hide use statements and main function boilerplate while running complete code as tests.
cargo doc
cargo doc generates HTML documentation from all doc comments in the project:
cargo doc --open
The generated documentation includes:
- API reference for all public items (functions, structs, enums, traits, modules)
- Type signatures, trait implementation listings
- Markdown rendering of doc comments
- Links to source code
- Search functionality
Furthermore, documentation for dependency crates is generated together. Documentation for all dependencies listed in Cargo.toml is generated locally, with cross-crate links properly resolved.
Intra-Doc Links
You can write links to other items within doc comments:
/// Has an interface similar to [`Vec`].
///
/// See [`Self::push`] for details.
///
/// [`HashMap`](std::collections::HashMap) is also worth referencing.
pub struct MyCollection {
// ...
}
rustdoc automatically resolves these links to the correct URLs. If the link target doesn't exist, a compiler warning is emitted.
docs.rs
docs.rs is a service that automatically hosts documentation for all crates published to crates.io.
When you cargo publish a crate, docs.rs automatically runs cargo doc and publishes the documentation. No additional work required from the crate author.
Every Rust crate's documentation is accessible at the uniform URL https://docs.rs/<crate-name>. This infrastructure is unique to Rust and doesn't exist in the Node.js (npm) or Python (PyPI) ecosystems.
The Ecosystem Supporting Documentation Quality
Clippy's Documentation-Related Lints
Clippy includes lints related to public API documentation:
// clippy::missing_docs_in_private_items (pedantic)
// Warns when pub items lack doc comments
// clippy::missing_errors_doc
// Warns when pub functions returning Result lack a # Errors section
// clippy::missing_panics_doc
// Warns when pub functions that can panic lack a # Panics section
The #[doc] Attribute
Doc comments are actually syntactic sugar for the #[doc] attribute:
/// This is a doc comment
pub fn foo() {}
// The above is equivalent to:
#[doc = "This is a doc comment"]
pub fn foo() {}
Using this fact, you can also generate documentation programmatically:
macro_rules! make_documented_fn {
($name:ident, $doc:expr) => {
#[doc = $doc]
pub fn $name() {}
};
}
make_documented_fn!(hello, "A function that greets.");
Summary
The beauty of Rust's documentation system lies not in individual features, but in how they work together as an integrated ecosystem:
| Element | Role |
|---|---|
/// / //! |
Markdown doc comments |
| Doc tests | Code examples are automatically tested |
cargo doc |
HTML documentation generation |
| docs.rs | Automatic hosting of published crate docs |
| Clippy | Detects missing documentation |
| Intra-doc links | Automatic resolution of cross-item references |
Through this integration, the quality of documentation across the Rust ecosystem is remarkably high compared to other languages. Many crates on crates.io have thorough API documentation and tested code examples.
Documentation is often the quintessential "should be written but isn't." Rust addressed this with a design where "writing documentation naturally becomes writing tests." Guaranteeing correctness while maintaining convenience -- this too is a manifestation of Rust's design philosophy.