14.5Doc tests

Last updated June 13, 2026

Lesson 13.7 dropped a remarkable claim: the code examples in your doc comments are run as tests. This lesson collects on it. Doc tests are the example code blocks inside /// comments, and cargo test compiles and runs every one of them. The result is documentation that physically cannot go stale, a feature few languages offer and Rust treats as routine.

The example is the test

Recall the doc-comment example from lesson 13.7:

/// Adds two numbers together.
///
/// # Examples
///
/// ```
/// use my_crate::add;
///
/// assert_eq!(add(2, 3), 5);
/// ```
pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

When you run cargo test, the framework extracts the code inside that ``` block, compiles it as a tiny standalone program, and runs it. The assert_eq! inside is a real assertion: if add ever stops returning 5 for add(2, 3), this doc test fails. cargo test reports doc tests in their own section after the unit and integration tests:

   Doc-tests my_crate

running 1 test
test src/lib.rs - add (line 5) ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.15s

The test is named by the file and line where the example lives, so a failure points you straight at the doc comment that's now lying. Note the example uses use my_crate::add;: doc-test code runs as if it were an external user of your crate, so it imports from the crate by name, the same external-user vantage point as integration tests (lesson 14.4).

Key insight

A doc test makes your documentation and your code keep each other honest. The chronic failure of example code in docs is that it rots: the API changes, the example doesn't, and the docs now teach something that no longer works. Because Rust runs the example as a test, the moment it falls out of sync the build fails. Your examples are guaranteed to compile and produce the stated results, which is why Rust libraries' docs are unusually trustworthy.

Doc tests are documentation first

The reason doc tests are worth writing, beyond catching regressions, is that a good example is the single most useful piece of documentation a function can have. Prose describes; an example shows. A reader landing on add's docs sees, in three lines, how to call it and what to expect, and they can trust those three lines absolutely because the build verifies them. Write the example for the reader first, and get the test for free.

This shapes how you write them. A doc-test example should read like the clearest possible usage, not like an exhaustive test suite. Show the common case plainly. Put the edge cases and the thoroughness in your unit tests (lesson 14.3), where being exhaustive doesn't clutter the documentation. The doc test's job is to be an example that happens to be checked.

Hiding setup lines

Sometimes an example needs a line of setup that's necessary to compile but distracting to read (an import, a bit of boilerplate). Prefix such a line with # inside the doc-comment code block and it runs but doesn't appear in the rendered documentation:

/// Doubles a number.
///
/// ```
/// # use my_crate::double;
/// assert_eq!(double(21), 42);
/// ```
pub fn double(n: i32) -> i32 {
    n * 2
}

The # use my_crate::double; line is compiled and run (so the example actually works) but hidden from readers, who see just the meaningful assert_eq!. This keeps examples clean without making them fail to compile. Use it for the unavoidable plumbing, never to hide something the reader genuinely needs to understand the example.

Testing examples that should fail

Occasionally you want to show code that shouldn't compile (demonstrating a misuse the type system catches) or that panics. Annotations on the code block handle these: ```should_panic runs the example and expects a panic, ```compile_fail expects it to fail compilation, and ```ignore includes the code in the docs but skips running it (use sparingly, since an ignored example isn't verified). These are the doc-test equivalents of #[should_panic] from lesson 14.2, and you'll reach for them rarely, mostly when documenting exactly what the compiler won't let a user do.

Quiz time

Question #1

What does cargo test do with the code inside a # Examples block in a doc comment?

Show solution

It extracts the code, compiles it as a standalone program (as if an external user wrote it), and runs it, with any assertions checked. If the example fails to compile or an assertion fails, the doc test fails. This keeps documentation examples from going stale.

Question #2

What does prefixing a line in a doc-test code block with # do, and when should you use it?

Show solution

The line is compiled and run but hidden from the rendered documentation. Use it for necessary-but-distracting setup (imports, boilerplate) so the example still compiles and runs while the reader sees only the meaningful lines. Don't use it to hide something the reader needs to understand the example.

Question #3

Should a doc-test example be exhaustive, covering all the edge cases? Why or why not?

Show solution

No. A doc-test example should show the clearest common usage, because it's documentation first and a reader needs clarity, not coverage. Exhaustive edge-case checking belongs in unit tests (lesson 14.3), where being thorough doesn't clutter the docs. The doc test is an example that happens to be verified.

You now have three kinds of test (unit, integration, doc) all run by one command. The last lesson of the chapter covers running them well: filtering which tests run, seeing their output, ignoring slow ones, and how the test runner uses threads.