RustArena

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

//! 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:

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

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:

Previous: Debuggers and logging Next: Clippy: fixing common lints