Step 4 of 7
Documenting code with rustdoc
Rust has documentation built into the language and the toolchain. Every crate on crates.io gets its docs published automatically on docs.rs, and they all look the same because they're all made by rustdoc from comments in the code.
Doc comments
///documents the item that follows it (a function, struct, field, enum variant, method...). It's an outer doc comment.//!documents the item it's inside: written at the top oflib.rsormain.rsit documents the whole crate; at the top of a module file ormodblock, that module. It's an inner doc comment.- Doc comments are Markdown:
`code`, bold, lists, links, and fenced code blocks. - The first line (or paragraph) is the summary, shown in lists and search results. Keep it to one sentence.
//! Shipping cost calculations for the arena shop.
/// A parcel waiting to be shipped.
struct Parcel {
/// Weight in grams.
weight_g: u32,
}
/// Returns the shipping cost of `parcel` in cents.
///
/// Parcels up to 1 kg cost a flat 499; every started kilogram after that adds 150.
///
/// # Examples
///
/// ```
/// let cost = shipping_cost(&Parcel { weight_g: 2500 });
/// assert_eq!(cost, 799);
/// ```
fn shipping_cost(parcel: &Parcel) -> u32 {
let extra_kg = parcel.weight_g.saturating_sub(1000).div_ceil(1000);
499 + extra_kg * 150
}
fn main() {
for weight_g in [800, 1000, 2500] {
println!("{weight_g} g: {} cents", shipping_cost(&Parcel { weight_g }));
}
}
800 g: 499 cents
1000 g: 499 cents
2500 g: 799 cents
The comments don't change what the program does; they're for readers, and for cargo doc.
Standard sections
Rust docs use a few headings that readers look for:
# Examples: how to call it. The most-read section; include one for every public function.# Errors: for functions returningResult, which situations return which errors.# Panics: the inputs that make it panic, so callers can avoid them.# Safety: forunsafe fn, what the caller must guarantee.
Link to other items with square brackets and backticks, like [`Parcel`] or [`Vec::push`]. rustdoc turns them into links (intra-doc links) and warns if the target doesn't exist.
Doc tests
Code blocks in the doc comments of a library crate are doc tests: cargo test compiles and runs each one as a small program that uses your crate from the outside. So an example can only use public items, and it names them through the crate, usually with a use line (use shop::shipping_cost;). If the code panics (say, an assert_eq! fails) or no longer compiles, the test fails, so examples can't quietly go out of date. Mark a block ```no_run to compile it without running (it talks to a network, say), or ```ignore to skip it. Lines starting with # are compiled but hidden from the rendered docs, handy for setup code like that use line. (Doc tests need a Cargo library project; on this site the single-file compiler treats them as plain comments, so the examples here leave out the use line.)
Building the docs
$ cargo doc --open # your crate and all its dependencies, opened in a browser
$ cargo doc --no-deps # only your crate
$ cargo test --doc # run only the doc tests
Making docs required
Libraries usually add a crate-level attribute that turns the missing_docs lint on: every public item must have a doc comment. With deny it's an error, which is what this step's exercises use (the site compiles with rustc, so this is how it checks your docs):
#![deny(missing_docs)]
//! Inventory helpers.
pub fn restock(qty: u32, added: u32) -> u32 {
qty + added
}
fn main() {
println!("{}", restock(3, 4));
}
error: missing documentation for a function
Private items aren't checked: docs matter most where other code uses yours. Many teams use #![warn(missing_docs)] locally and turn warnings into errors in CI.
Common mistakes
//!in the wrong place: inner doc comments must come before any items in the file or module, or you geterror[E0753]: expected outer doc comment.- Docs that repeat the signature ("Takes a u32 and returns a u32"). Say what it means, the units, and the edge cases.
- An example that doesn't compile. As a doc test it fails
cargo test; that's the point.
Your task
The Shelf API works, but #![deny(missing_docs)] refuses to compile it. Document every public item: the struct, both fields, new, and take. Give take an # Errors section (when does it return Err?) and an # Examples section with a short code example.
Challenge 2
Write and document split_bill(total_cents, people) -> Vec<u32>: it splits a bill so the shares differ by at most one cent, and the first people in the list pay the extra cents (1000 between 3 people is [334, 333, 333]). It panics if people is 0.
The file has #![deny(missing_docs)]: write a crate-level //! line, and give split_bill a summary, an # Examples section and a # Panics section.
Challenge 3
Modules are documented with //! inside them. Document the scoring module (a //! line as the first thing in its block) and its items, and implement them:
WIN_POINTS: a win is worth 3 points.points(wins, draws): 3 points per win, 1 per draw.rank(points):"bronze"below 10 points,"silver"from 10 to 19,"gold"from 20.
Previous: Debuggers and logging Next: Clippy: fixing common lints