Generating and publishing Rust documentation with cargo doc is surprisingly straightforward, but the real magic happens when you understand how it integrates with the Rust ecosystem to create discoverable, versioned documentation for your crates.
Let’s see it in action. Imagine you have a simple Rust crate named my_awesome_lib.
// src/lib.rs
/// This is a great function that adds two numbers.
///
/// # Examples
///
/// ```
/// assert_eq!(my_awesome_lib::add(2, 2), 4);
/// ```
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
/// This is a struct that holds a value.
pub struct Container {
value: i32,
}
impl Container {
/// Creates a new container.
pub fn new(value: i32) -> Self {
Container { value }
}
/// Gets the value from the container.
pub fn get_value(&self) -> i32 {
self.value
}
}
To generate the documentation locally, you’d run:
cargo doc --open
This command compiles your crate and all its dependencies, generating HTML files in the target/doc directory. The --open flag conveniently launches your default web browser to the index.html file, showing you the documentation for my_awesome_lib. You’ll see your add function and Container struct, complete with their doc comments rendered beautifully, including the example code which is automatically run as a test.
The core problem cargo doc solves is making Rust code understandable and accessible. Without it, developers would have to dig through source code to figure out how to use a crate. cargo doc transforms your written intent in doc comments into a navigable, searchable website. Internally, it uses rustdoc, the same tool that powers the official Rust documentation at doc.rust-lang.org. It parses your code, extracts public items and their associated doc comments, and then uses Markdown and HTML templating to present them in a user-friendly format.
You control the documentation through attributes in your Rust code. The /// syntax denotes outer documentation comments, which apply to the item that follows. These comments support Markdown, allowing you to format text, create lists, and even include code blocks. The # Examples section is special: rustdoc will extract these code blocks and compile/run them as part of your crate’s tests, ensuring your examples are always up-to-date. For more complex scenarios, you can use # Panics to explain when a function might panic, # Errors for custom error types, and # Safety for unsafe functions.
Publishing these docs to the web is typically done via docs.rs. This is a service that automatically builds and hosts documentation for crates on crates.io. When you publish a new version of your crate to crates.io, docs.rs detects it, pulls the source code, runs cargo doc, and deploys the generated HTML to a URL like https://docs.rs/my_awesome_lib/0.1.0/my_awesome_lib/. This makes your documentation immediately available to anyone who might want to use your crate.
Here’s a critical detail: cargo doc respects the visibility of your items. Only public items (pub) will be included in the generated documentation by default. If you want to document private items for internal use or debugging, you can use the --document-private-items flag: cargo doc --document-private-items. This is rarely done for published crates but can be invaluable for large internal libraries.
The most surprising thing about cargo doc’s integration with the ecosystem is how deeply it’s woven into the testing framework. When you run cargo test, rustdoc automatically compiles and runs all the code examples found within your doc comments. This isn’t just for presentation; it’s a powerful form of integration testing. If an example in your documentation fails to compile or run, cargo test will report it as a test failure, ensuring that your documentation stays accurate and functional as your code evolves. This mechanism is key to maintaining high-quality documentation for widely used crates.
Once your documentation is published on docs.rs, the next natural step is to consider how to manage documentation for different versions of your crate, especially when dealing with breaking changes.