Documentation Tests
Documentation tests (rustdoc --test, invoked via cargo test or cargo test --doc) verify that every Rust code block in your library's documentation compiles and passes its assertions.
Doc-Test Transformation Pipeline in `rustdoc`:
1. Extract fenced code block from `///` or `//!`
2. Strip leading `# ` prefixes (keeping those lines for compilation)
3. Insert `extern crate <my_crate>;`
4. If no `fn main` is present, wrap body inside `fn main() { ... }`
5. Compile and execute (merging compatible doc-tests into a single binary in Rust 2024!)
Rust 2024 Edition Merged Doc-Test Binaries
Prior to the Rust 2024 edition, rustdoc compiled every single documentation code block into its own separate executable binary—causing crates with hundreds of doc-tests to spend tens of seconds linking individual binaries.
In Rust 2024 edition, rustdoc automatically merges compatible doc-tests across the crate into a single combined test binary, cutting cargo test --doc build and execution times by an order of magnitude while still isolating tests that use compile_fail or global attributes (#![feature(...)]).
Controlling Doc-Test Execution with Fence Annotations
You can combine annotations on a triple-backtick fence using commas (for example, ```rust,no_run):
| Fence Tag | Compiled? | Executed? | Pass Condition |
|---|---|---|---|
``` or ```rust |
Yes | Yes | Returns normally (or Ok(())) without panicking. |
```should_panic |
Yes | Yes | Panics during execution. |
```no_run |
Yes | No | Compiles cleanly without type or borrow errors. |
```compile_fail |
Yes | No | Fails to compile (e.g., demonstrating borrow-checker safety). |
```edition2024 |
Yes | Yes | Compiles using the specified Rust edition. |
```ignore |
No | No | Skipped completely (displays a warning badge in cargo doc). |
```text / ```sh / ```json |
No | No | Rendered as non-Rust code or plain text. |
Writing compile_fail Doc-Tests for Type-State and Borrow Guarantees
When designing zero-cost abstractions—such as lifetimes tying an iterator to its parent buffer or a type-state builder that prevents calling .send() before .authenticate()—a compile_fail doc-test proves at test time that invalid code is rejected by the compiler:
/// A read-only view borrowed from a buffer.
///
/// The borrow checker prevents mutating the underlying buffer while `View` is alive:
///
/// ```compile_fail
/// use my_crate::View;
///
/// let mut buf = vec![1, 2, 3];
/// let view = View::new(&buf);
/// buf.push(4); // ❌ cannot borrow `buf` as mutable while `view` is alive
/// println!("{:?}", view.as_slice());
/// ```
pub struct View<'a> {
slice: &'a [i32],
}
impl<'a> View<'a> {
pub fn new(slice: &'a [i32]) -> Self {
Self { slice }
}
pub fn as_slice(&self) -> &'a [i32] {
self.slice
}
}
fn main() {
let buf = vec![1, 2, 3];
let view = View::new(&buf);
println!("View slice: {:?}", view.as_slice());
}
View slice: [1, 2, 3]
You can even specify an expected compiler error code on a compile_fail fence (such as ```compile_fail,E0502) so the doc-test only passes if compilation fails with that specific error code (E0502: mutable borrow while immutable borrow is active).