ExamplesBy LevelBy TopicLearning Paths
746 Fundamental

746-doc-test-patterns — Doc Test Patterns

Functional Programming

Tutorial Video

Text description (accessibility)

This video demonstrates the "746-doc-test-patterns — Doc Test Patterns" functional Rust example. Difficulty level: Fundamental. Key concepts covered: Functional Programming. Documentation that diverges from reality is worse than no documentation: it misleads users and erodes trust. Key difference from OCaml: 1. **First

Tutorial

The Problem

Documentation that diverges from reality is worse than no documentation: it misleads users and erodes trust. Rust's doc tests solve this by compiling and running every /// code example as a test. If you change the function signature or behavior, the doc test fails — documentation can never silently go out of date. This is used pervasively in the Rust standard library, the serde crate, and virtually every published Rust crate.

🎯 Learning Outcomes

  • • Write code examples in /// doc comments using fenced ` blocks
  • • Understand that cargo test runs all doc examples as real test cases
  • • Use # prefix to hide boilerplate lines in rendered docs while keeping them in the test
  • • Mark examples with no_run (compiles but skips execution) or ignore (skips entirely)
  • • Write doc tests that cover both success cases and documented error conditions
  • Code Example

    /// Clamps `x` to the inclusive range `[lo, hi]`.
    ///
    /// # Examples
    ///
    ///

    Key Differences

  • First-class support: Rust's cargo test --doc runs doc examples with no additional tooling; OCaml requires mdx or external tooling.
  • Compilation: Rust doc examples are fully compiled against the crate's API; OCaml's mdx interprets examples via the REPL without full type checking.
  • Visibility control: Rust's #-prefix hides boilerplate while keeping it executable; OCaml's mdx has no equivalent line-hiding syntax.
  • Integration: Rust doc tests appear in cargo test output alongside unit tests; OCaml doc tests require a separate mdx invocation.
  • OCaml Approach

    OCaml does not have a built-in doc-test runner. The mdx (Markdown eXtended) tool runs OCaml code blocks in markdown documentation. ocamldoc generates HTML from (** ... *) comments but does not execute examples. The doctests opam package provides pytest-style doc testing. Jane Street uses expect_test for inline tests with expected output embedded in source comments.

    Full Source

    #![allow(clippy::all)]
    //! # Documentation Tests
    //!
    //! Code examples in `///` doc comments are compiled and executed as tests.
    //! Documentation can never go out of date.
    
    /// Clamps `x` to the inclusive range `[lo, hi]`.
    ///
    /// # Examples
    ///
    /// ```
    /// use example_746_doc_test_patterns::clamp;
    /// assert_eq!(clamp(0, 10, -5), 0);
    /// assert_eq!(clamp(0, 10, 5), 5);
    /// assert_eq!(clamp(0, 10, 15), 10);
    /// // Boundaries are inclusive
    /// assert_eq!(clamp(0, 10, 0), 0);
    /// assert_eq!(clamp(0, 10, 10), 10);
    /// ```
    pub fn clamp(lo: i32, hi: i32, x: i32) -> i32 {
        x.max(lo).min(hi)
    }
    
    /// Repeats `s` exactly `n` times.
    ///
    /// # Examples
    ///
    /// ```
    /// use example_746_doc_test_patterns::repeat;
    /// assert_eq!(repeat("ab", 3), "ababab");
    /// assert_eq!(repeat("x", 0), "");
    /// assert_eq!(repeat("", 5), "");
    /// ```
    pub fn repeat(s: &str, n: usize) -> String {
        s.repeat(n)
    }
    
    /// Splits `s` on the first occurrence of `delim`.
    ///
    /// Returns `None` if `delim` is not found.
    ///
    /// # Examples
    ///
    /// ```
    /// use example_746_doc_test_patterns::split_once_char;
    /// assert_eq!(split_once_char("key:value", ':'), Some(("key", "value")));
    /// assert_eq!(split_once_char("no-delim", ':'), None);
    /// assert_eq!(split_once_char("a:b:c", ':'), Some(("a", "b:c")));
    /// ```
    pub fn split_once_char(s: &str, delim: char) -> Option<(&str, &str)> {
        s.split_once(delim)
    }
    
    /// Safe division that returns `Err` if divisor is zero.
    ///
    /// # Errors
    ///
    /// Returns `Err("division by zero")` when `b == 0`.
    ///
    /// # Examples
    ///
    /// ```
    /// use example_746_doc_test_patterns::safe_div;
    /// assert_eq!(safe_div(10, 2), Ok(5));
    /// assert_eq!(safe_div(10, 0), Err("division by zero"));
    /// assert_eq!(safe_div(-9, 3), Ok(-3));
    /// ```
    pub fn safe_div(a: i64, b: i64) -> Result<i64, &'static str> {
        if b == 0 {
            Err("division by zero")
        } else {
            Ok(a / b)
        }
    }
    
    /// Computes factorial of n.
    ///
    /// # Panics
    ///
    /// Panics if `n` is zero (this implementation treats 0! as undefined).
    ///
    /// # Examples
    ///
    /// ```
    /// use example_746_doc_test_patterns::factorial;
    /// assert_eq!(factorial(1), 1);
    /// assert_eq!(factorial(5), 120);
    /// ```
    ///
    /// Attempting to compute factorial(0) will panic:
    ///
    /// ```should_panic
    /// example_746_doc_test_patterns::factorial(0);
    /// ```
    pub fn factorial(n: u64) -> u64 {
        if n == 0 {
            panic!("factorial(0) is undefined in this implementation")
        }
        (1..=n).product()
    }
    
    /// Alternative factorial that handles 0 correctly.
    ///
    /// # Examples
    ///
    /// ```
    /// use example_746_doc_test_patterns::factorial_safe;
    /// assert_eq!(factorial_safe(0), 1);
    /// assert_eq!(factorial_safe(1), 1);
    /// assert_eq!(factorial_safe(5), 120);
    /// assert_eq!(factorial_safe(10), 3628800);
    /// ```
    pub fn factorial_safe(n: u64) -> u64 {
        if n == 0 {
            1
        } else {
            (1..=n).product()
        }
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
    
        #[test]
        fn test_clamp_edge_cases() {
            assert_eq!(clamp(i32::MIN, i32::MAX, 0), 0);
            assert_eq!(clamp(5, 5, 100), 5); // lo == hi
        }
    
        #[test]
        fn test_repeat_unicode() {
            assert_eq!(repeat("🦀", 3), "🦀🦀🦀");
        }
    
        #[test]
        fn test_safe_div_negative() {
            assert_eq!(safe_div(-10, -2), Ok(5));
        }
    
        #[test]
        fn test_split_empty_string() {
            assert_eq!(split_once_char("", ':'), None);
        }
    
        #[test]
        fn test_factorial_safe_large() {
            assert_eq!(factorial_safe(12), 479001600);
        }
    }
    ✓ Tests Rust test suite
    #[cfg(test)]
    mod tests {
        use super::*;
    
        #[test]
        fn test_clamp_edge_cases() {
            assert_eq!(clamp(i32::MIN, i32::MAX, 0), 0);
            assert_eq!(clamp(5, 5, 100), 5); // lo == hi
        }
    
        #[test]
        fn test_repeat_unicode() {
            assert_eq!(repeat("🦀", 3), "🦀🦀🦀");
        }
    
        #[test]
        fn test_safe_div_negative() {
            assert_eq!(safe_div(-10, -2), Ok(5));
        }
    
        #[test]
        fn test_split_empty_string() {
            assert_eq!(split_once_char("", ':'), None);
        }
    
        #[test]
        fn test_factorial_safe_large() {
            assert_eq!(factorial_safe(12), 479001600);
        }
    }

    Deep Comparison

    OCaml vs Rust: Documentation Tests

    Doc Comment Examples

    OCaml (odoc)

    (**
      Clamp a value within [lo, hi].
    
      @example
      {[
        let _ = clamp 0 10 (-5)  (* = 0 *)
        let _ = clamp 0 10 5     (* = 5 *)
      ]}
    *)
    let clamp lo hi x = max lo (min hi x)
    

    Note: OCaml's odoc does NOT execute these examples.

    Rust (rustdoc)

    /// Clamps `x` to the inclusive range `[lo, hi]`.
    ///
    /// # Examples
    ///
    /// 

    /// use my_crate::clamp; /// assert_eq!(clamp(0, 10, -5), 0); /// assert_eq!(clamp(0, 10, 5), 5); ///

    pub fn clamp(lo: i32, hi: i32, x: i32) -> i32 {
        x.max(lo).min(hi)
    }
    
    **These examples are compiled and run by cargo test!**

    Hidden Setup Lines (Rust Only)

    /// # Examples
    ///
    /// 

    /// # use my_crate::helper; // hidden in rendered docs /// let result = helper(); /// assert!(result.is_ok()); ///

    The # prefix includes the line in compilation but hides it in documentation.

    Testing Panics

    OCaml

    let test_panic () =
      try
        let _ = factorial 0 in
        failwith "expected exception"
      with Invalid_argument _ -> ()
    

    Rust

    /// 
    should_panic /// my_crate::factorial(0); // this line panics ///

    Key Differences

    FeatureOCamlRust
    Doc examples executed❌ No✅ Yes, by cargo test
    Hidden setup lines❌ No# use ... syntax
    Panic testingManual try/catchshould_panic attribute
    Compile-fail tests❌ Nocompile_fail attribute
    DiscoveryManual odoc setupAutomatic with rustdoc

    Why Rust's Approach Is Better

  • Examples can't lie — if API changes, doc tests fail
  • Copy-paste friendly — users know examples work
  • No maintenance burden — docs stay in sync automatically
  • Panic behavior documentedshould_panic proves the documented behavior
  • Exercises

  • Add a /// doc example to split_once_char that shows the ? operator usage in a function that returns Option, including the hidden fn main() -> Option<()> wrapper.
  • Write a parse_ip function with doc examples for IPv4, IPv6, and malformed inputs, using no_run for any example that requires network access.
  • Add an Examples section to a Config::from_str function with a multi-step example that shows parsing, validation, and accessing fields — verify it compiles and runs with cargo test --doc.
  • Open Source Repos