24.6 Documentation Tests (doctests)

Rust includes a powerful feature where code examples written inside documentation comments (/// for items, //! for modules/crates) can be compiled and run as tests. This ensures that your documentation examples remain accurate and functional as the underlying code evolves.

/// Calculates the factorial of a non-negative integer.
///
/// Panics if the input `n` is negative.
///
/// # Examples
///
/// ```
/// # use my_crate::factorial; // Hidden setup line
/// assert_eq!(factorial(0), 1);
/// assert_eq!(factorial(5), 120);
/// ```
///
/// This example demonstrates the panic condition:
/// ```should_panic
/// # use my_crate::factorial;
/// // Factorial is not defined for negative numbers
/// factorial(-1);
/// ```
///
/// Example showing compilation only (e.g., for demonstrating type signatures):
/// ```no_run
/// # use my_crate::factorial;
/// let f6: u64 = factorial(6);
/// // No assertion, just compile check.
/// ```
///
/// This block is ignored by the test runner and documentation generator:
/// ```ignore
/// This is not Rust code. It won't be tested or rendered.
/// ```
pub fn factorial(n: i64) -> u64 {
    if n < 0 {
        panic!("Factorial input cannot be negative");
    }
    let mut result: u64 = 1;
    for i in 1..=(n as u64) {
        result = result.saturating_mul(i); // Use saturating_mul for safety
    }
    result
}

When cargo test runs, it extracts these code blocks:

  1. It automatically adds extern crate my_crate; (using your crate’s name) if needed.
  2. It often wraps the code block in fn main() { ... }.
  3. It compiles and runs the code according to the block’s attributes.
  • Assertions: Standard assert! macros work within doctests.
  • Hidden Lines: Lines starting with # (hash space) are executed during testing but are hidden in the rendered HTML documentation (cargo doc --open). This is ideal for including necessary use statements or setup code that would otherwise clutter the example.
  • Attributes: Placed after the opening ```:
    • ``` (no attribute): The code must compile and run successfully (without panicking).
    • ```should_panic: The code must compile and must panic when run.
    • ```no_run: The code must compile, but it is not executed. Useful for examples involving actions with side effects (like filesystem or network operations) or just demonstrating API usage patterns.
    • ```ignore: The code block is completely ignored by cargo test and cargo doc.

Doctests are excellent for verifying basic usage examples of your public API but are generally not suitable for complex test scenarios or testing internal implementation details, for which unit or integration tests are preferred.