Doc Tests in Rust and Keeping Examples Honest Step by Step
Code blocks in doc comments compile and run as tests. Write examples that stay correct, hide setup lines with a hash, mark intentional failures and choose the right attributes.

Documentation examples that no one runs are lies waiting to happen. Rust solves it by making every example a test. The cost is a slower cargo test and a few conventions for hiding boilerplate. This post covers what to write and what the attributes do. It is the third kind of test in the testing in Rust guide, and it supports writing the examples needed for public API design.
Rust 1.90.
A doc test
/// Splits `"host:port"` into its parts.
///
/// # Examples
///
/// ```
/// use my_crate::split_host_port;
///
/// let (host, port) = split_host_port("example.test:8080").unwrap();
/// assert_eq!(host, "example.test");
/// assert_eq!(port, 8080);
/// ```
pub fn split_host_port(s: &str) -> Option<(&str, u16)> {
let (host, port) = s.rsplit_once(':')?;
Some((host, port.parse().ok()?))
}cargo test runs it and reports Doc-tests my_crate with 1 passed. If someone changes the function's behavior without updating the example, the test fails. The code is shown in the generated docs exactly as written.
Hide the setup
Lines that begin with # run but are not displayed:
/// ```
/// # use my_crate::split_host_port;
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let (host, _) = split_host_port("a:1").ok_or("no colon")?;
/// assert_eq!(host, "a");
/// # Ok(())
/// # }
/// ```The reader sees two lines and the test is a complete program. This is how ? works in examples: the hidden wrapper returns a Result.
Attributes on the fence
| Attribute on the opening fence | Meaning |
|---|---|
no_run | compile but do not run, for network or disk |
ignore | skip entirely, avoid it, since nothing checks it |
should_panic | pass if it panics |
compile_fail | pass only if it fails to compile |
text or bash | not Rust, not tested |
edition2021 | compile with a specific edition |
The compile_fail attribute is useful to show what the type system forbids, and it is fragile: any compile error passes it, including a typo. Add an error code after it, such as compile_fail,E0382, to pin the reason.
Panics and errors
Document the conditions in # Panics, # Errors and # Safety sections, as in safety comments. Give a doc test for the interesting case, not only the happy path.
Speed
Each doc test is compiled and linked separately. A crate with 200 doc tests can spend a long time on that. Rustdoc merges tests in edition 2024 to cut it down. Keep examples short, and move long scenarios into integration tests (integration tests).
Binary crates
Doc tests do not run for main.rs-only packages. Put the logic in a library crate to document and test it this way (library or binary).
Where doc tests stop
They show usage and cannot test private code or complex setups. Run just them with cargo test --doc. The rustdoc book documents all attributes under documentation tests.


