# A Systematic Guide to Effective, Ergonomic, and DRY Doctests in Rust
## Project policy (Wireframe)
Wireframe enforces doctests as a first-class quality gate.
- Run doctests with `make test-doc` (equivalent to
`RUSTFLAGS="-D warnings" cargo test --doc --all-features`).
- Keep doctest ownership on the public API surface only. Private modules and
`#[cfg(test)]` helpers may include examples, but those blocks should use
non-doctest fences such as `plaintext`.
- Enforce executable documentation balance with
`make doctest-benchmark`. The benchmark fails when runnable doctests drop
below 70% of runnable-or-`no_run` blocks, or when `no_run` blocks exceed 30%.
## The `rustdoc` Compilation Model: A Foundational Perspective
To master the art of writing effective documentation tests in Rust, one must
first understand the foundational principles upon which the `rustdoc` tool
operates. Its behaviour, particularly its testing mechanism, is not an
arbitrary collection of features but a direct consequence of a deliberate
design philosophy. The core of this philosophy is that every doctest should
validate the public API of a crate from the perspective of an external user.
This single principle dictates the entire compilation model and explains both
the power and the inherent limitations of doctests.
### 1.1 The "Separate Crate" Paradigm
At its heart, `rustdoc` treats each documentation test not as a snippet of code
running within the library's own context, but as an entirely separate,
temporary crate.[^1] When a developer executes
`cargo test --doc`, `rustdoc` initiates a multi-stage process for every code
block found in the documentation comments1. **Parsing and Extraction**: `rustdoc` first parses the source code of the
library, resolving conditional compilation attributes (`#[cfg]`) to
determine which items are active and should be documented for the current
target. It then extracts all code examples enclosed in triple-backtick
fences (\`\`\`).
2. **Code Generation**: For each extracted code block, `rustdoc` performs a
textual transformation to create a complete, self-contained Rust program. If
the block does not already contain a `fn main()`, the code is wrapped within
one. Crucially, `rustdoc` also injects an `extern crate <mycrate>;`
statement, where `<mycrate>` is the name of the library being documented.
This makes the library under test available as an external dependency.
3. **Individual Compilation**: `rustdoc` then invokes the Rust compiler
(`rustc`) separately for *each* of these newly generated miniature programs.
Each one is compiled and linked against the already-compiled version of the
main library.
4. **Execution and Verification**: Finally, if compilation succeeds, the
resulting executable is run. The test is considered to have passed if the
program runs to completion without panicking. The executable is then
deleted.
The significance of this model cannot be overstated. It effectively transforms
every doctest into a true integration test.[^6] The test code does not have
special access to the library's internals; it interacts with the library's API
precisely as a downstream crate would, providing a powerful guarantee that the
public-facing examples are correct and functional.[^1]
### 1.2 First-Order Consequences of the Model
This "separate crate" paradigm has two immediate and significant consequences
that shape all advanced doctesting patterns.
First, **API visibility is strictly limited to public items**. Because the
doctest is compiled as an external crate, it can only access functions,
structs, traits, and modules marked with the `pub` keyword. It has no access to
private items or even crate-level public items (e.g., `pub(crate)`). This is
not a bug or an oversight but a fundamental aspect of the design, enforcing the
perspective of an external consumer.[^1]
Second, the model has **profound performance implications**. The process of
invoking `rustc` to compile and link a new executable for every single doctest
is computationally expensive. For small projects, this overhead is negligible.
However, for large libraries with hundreds of doctests, the cumulative
compilation time can become a significant bottleneck in the development and
CI/CD cycle, a common pain point in the Rust community.[^2]
The architectural purity of the `rustdoc` model—its insistence on simulating an
external user—creates a fundamental trade-off. On one hand, it provides an
unparalleled guarantee that the public documentation is accurate and that the
examples work as advertised, creating true "living documentation".[^7] On the
other hand, this same purity prevents the use of doctests for verifying
documentation of internal, private APIs. This forces a bifurcation of
documentation strategy. Public-facing documentation can be tied directly to
working, tested code. Internal documentation for maintainers, which is equally
vital for a project's health, cannot be verified with the same tools. Examples
for private functions must either be marked as
`ignore`, forgoing the test guarantee, or be duplicated in separate unit tests,
violating the "Don't Repeat Yourself" (DRY) principle.[^1] This reveals that
`rustdoc`'s design implicitly prioritizes the integrity of the public contract
over the convenience of a single, unified system for testable documentation of
both public and private code.
## Authoring Effective Doctests: From Basics to Best Practices
With a solid understanding of the `rustdoc` compilation model, one can move on
to the practical craft of authoring doctests. An effective doctest is more than
just a block of code; it is a piece of technical communication that should be
clear, illustrative, and robust.
### 2.1 The Anatomy of a Doctest
Doctests reside within documentation comments. Rust recognizes two types:
- **Outer doc comments (`///`)**: These document the item that follows them
(e.g., a function, struct, or module). This is the most common type.
- **Inner doc comments (`//!`)**: These document the item they are inside
(e.g., a module or the crate itself). They are typically used at the top of
`lib.rs` or `mod.rs` to provide crate- or module-level documentation.
Within these comments, a code block is
denoted by triple back-ticks (```). While `rustdoc` defaults to Rust syntax,
explicitly add the `rust` language specifier for clarity.[^3] A doctest
"passes" when it compiles and runs without panicking. To assert specific
outcomes, use the standard macros `assert!`, `assert_eq!`, and
`assert_ne!`.[^3] <!-- markdownlint-enable MD013 -->
### 2.2 The Philosophy of a Good Example
The purpose of a documentation example extends beyond merely demonstrating
syntax. A reader can typically be expected to understand the mechanics of
calling a function or instantiating a struct. A truly valuable example
illustrates *why* and in *what context* an item should be used.[^9] It should
tell a small story or solve a miniature problem that illuminates the item's
purpose. For instance, an example for
`String::clone()` should not just show `hello.clone();`, but should demonstrate
a scenario where ownership rules necessitate creating a copy.[^9]
To achieve this, examples must be clear and concise. Any code that is not
directly relevant to the point being made—such as complex setup, boilerplate,
or unrelated logic—should be hidden to avoid distracting the reader.[^3]
### 2.3 Ergonomic Error Handling: Taming the `?` Operator
One of the most common ergonomic hurdles in writing doctests involves handling
functions that return a `Result`. The question mark (`?`) operator is the
idiomatic way to propagate errors in Rust, but it presents a challenge for
doctests. The implicit `fn main()` wrapper generated by `rustdoc` has a return
type of `()`, while the `?` operator can only be used in a function that
returns a `Result` or `Option`. This mismatch leads to a compilation error.[^3]
Using `.unwrap()` or `.expect()` in examples is strongly discouraged. It is
considered an anti-pattern because users often copy example code verbatim, and
encouraging panicking on errors is contrary to robust application design.[^9]
Instead, two canonical solutions exist.
Solution 1: The Explicit main Function
The most transparent and recommended approach is to manually write a main
function within the doctest that returns a Result. This leverages the
Termination trait, which is implemented for Result. The surrounding boilerplate
can then be hidden from the rendered documentation.
```Rust
/// # Examples
///
/// ```
/// # use std::error::Error;
/// #
/// # fn main() -> Result<(), Box<dyn Error>> {
/// let config = "key=value".parse::<MyConfig>()?;
/// assert_eq!(config.get("key"), Some("value"));
/// #
/// # Ok(())
/// # }
/// ```
```
In this pattern, the reader only sees the core, fallible code, while the test
itself is a complete, well-behaved program.[^9]
Solution 2: The Implicit Result-Returning main
rustdoc provides a lesser-known but more concise shorthand for this exact
scenario. If a code block ends with the literal token (()), rustdoc will
automatically wrap the code in a main function that returns a Result.
```Rust
/// # Examples
///
/// ```
/// let config = "key=value".parse::<MyConfig>()?;
/// assert_eq!(config.get("key"), Some("value"));
/// (()) // Note: No whitespace between parentheses
/// ```
```
This is functionally equivalent to the explicit `main` but requires less
boilerplate. However, it is critical that the `(())` be written as a single,
contiguous sequence of characters, as `rustdoc`'s detection mechanism is purely
textual and will not recognize `( () )`.[^3]
### 2.4 The Power of Hidden Lines (`#`): Creating Clean Examples
The mechanism that makes clean, focused examples possible is the "hidden line"
syntax. Any line in a doctest code block that begins with a `#` character
(optionally preceded by whitespace) will be compiled and executed as part of
the test, but it will be completely omitted from the final HTML documentation
rendered for the user.[^3]
This feature is essential for bridging the gap between what makes a good,
human-readable example and what constitutes a complete, compilable program. Its
primary use cases include:
1. **Hiding** `main` **Wrappers**: As demonstrated in the error-handling
examples, the entire `fn main() -> Result<...> {... }` and `Ok(())`
scaffolding can be hidden, presenting the user with only the relevant
code.
2. **Hiding Setup Code**: If an example requires some preliminary setup—like
creating a temporary file, defining a helper struct for the test, or
initializing a server—this logic can be hidden to keep the example focused
on the API item being documented.
3. **Hiding** `use` **Statements**: While often useful to show which types are
involved, `use` statements can sometimes be hidden to declutter simple
examples.
The existence of features like hidden lines and the `(())` shorthand reveals a
core tension in `rustdoc`'s design. The compilation model is rigid: every test
must be a valid, standalone program.[^2] However, the ideal documentation
example is often just a small, illustrative snippet that is not a valid program
on its own.[^9] These ergonomic features are pragmatic "patches" designed to
resolve this conflict. They allow the developer to inject the necessary
boilerplate to satisfy the compiler without burdening the human reader with
irrelevant details. Understanding them as clever workarounds, rather than as
first-class language features, helps explain their sometimes quirky, text-based
behaviour.
## Advanced Doctest Control and Attributes
Beyond basic pass/fail checks, `rustdoc` provides a suite of attributes to
control doctest behaviour with fine-grained precision. These attributes, placed
in the header of a code block (e.g., \`\`\`\`ignore\`), allow developers to
handle expected failures, non-executable examples, and other complex scenarios.
### 3.1 A Comparative Analysis of Doctest Attributes
Choosing the correct attribute is critical for communicating the intent of an
example and ensuring the test suite provides meaningful feedback. The following
table provides a comparative reference for the most common doctest attributes.
| ignore | Skips both compilation and execution. | ignored | Use Case: For pseudocode, examples known to be broken, or to temporarily disable a test. Warning: Provides no guarantee that the code is even syntactically correct. Generally discouraged in favour of more specific attributes. |
| should_panic | Compiles and runs the code. The test passes if the code panics. | OK on panic, failed if it does not panic. | Use Case: Demonstrating functions that are designed to panic on invalid input (e.g., indexing out of bounds). |
| compile_fail | Attempts to compile the code. The test passes if compilation fails. | OK on compilation failure, failed if it compiles successfully. | Use Case: Illustrating language rules, such as the borrow checker or type system constraints. Warning: Highly brittle. A future Rust version might make the code valid, causing the test to unexpectedly fail. |
| no_run | Compiles the code but does not execute it. | OK if compilation succeeds. | Use Case: Essential for examples with undesirable side effects in a test environment, such as network requests, filesystem I/O, or launching a GUI. Guarantees the example is valid Rust code without running it. |
| edition20xx | Compiles the code using the specified Rust edition's rules. | OK on success. | Use Case: Demonstrating syntax or idioms that are specific to a particular Rust edition (e.g., edition2018, edition2021). |
### 3.2 Detailed Attribute Breakdown
- `ignore`: This is the bluntest instrument in the toolbox. It tells `rustdoc`
to do nothing with the code block. It is almost always better to either fix
the example using hidden lines or use a more descriptive attribute like
`no_run`. Its main legitimate use is for non-Rust code blocks or
illustrative pseudocode.
- `should_panic`: This attribute inverts the normal test condition. It is used
to document and verify behaviour that intentionally results in a panic. The
test will fail if the code completes successfully or panics for a reason
other than the one expected (if a specific panic message is asserted).
- `compile_fail`: This is a powerful tool for creating educational examples
that demonstrate what *not* to do. It is frequently used in documentation
about ownership, borrowing, and lifetimes to show code that the compiler will
correctly reject. However, developers must be aware of its fragility. An
evolution in the Rust language or compiler could make previously invalid code
compile, which would break the test.
- `no_run`: This attribute strikes a crucial balance between test verification
and practicality. For an example that demonstrates how to download a file
from the internet, the example code must be syntactically correct and use the
API properly, but it is undesirable for the CI server to perform a network
request during every test run. `no_run` provides this guarantee by compiling
the code without executing it.
- `edition20xx`: This attribute allows an example to be tested against a
specific Rust edition. This is important for crates that support multiple
editions and need to demonstrate edition-specific features or migration
paths.
## The DRY Principle in Doctests: Managing Shared and Complex Logic
The "Don't Repeat Yourself" (DRY) principle is a cornerstone of software
engineering, and it applies to test code as much as it does to production code.
As a project grows, it is common for multiple doctests to require the same
complex setup logic. Copying and pasting this setup into every doctest using
hidden lines is tedious, error-prone, and a clear violation of the DRY
principle.
### 4.1 The Problem of Shared Setup
Consider a library for interacting with a database. Nearly every doctest might
need to perform the same initial steps: spin up a temporary database instance,
connect to it, and seed it with some initial data. Repeating this multi-line
setup in every single example is inefficient and makes maintenance difficult. A
change to the setup process would require updating dozens of doctests.
### 4.2 The `#[cfg(doctest)]` Pattern for Shared Helpers
The canonical solution to this problem involves using a special configuration
flag provided by `rustdoc`: `doctest`. A common mistake is to try to place
shared test logic in a block guarded by `#[cfg(test)]`. This will not work,
because `rustdoc` does not enable the `test` configuration flag during its
compilation process; `#[cfg(test)]` is reserved for unit and integration tests
run directly by `cargo test`.[^11]
Instead, `rustdoc` sets its own unique `doctest` flag. By guarding a module or
function with `#[cfg(doctest)]`, developers can write helper code that is
compiled and available *only* when `cargo test --doc` is running. This code is
excluded from normal production builds and standard unit test runs, preventing
any pollution of the final binary or the public API.
The typical implementation pattern is to create a private helper module within
the library:
```Rust
// In lib.rs or a submodule
/// A function that requires a complex environment to test.
///
/// # Examples
///
/// ```
/// # use crate::doctest_helpers::setup_test_environment;
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let mut ctx = setup_test_environment()?;
/// let result = my_func_that_needs_env(&mut ctx);
/// assert!(result.is_ok());
/// # Ok(())
/// # }
/// ```
pub fn my_func_that_needs_env(ctx: &mut TestContext) -> Result<(), ()> {
//... function logic...
Ok(())
}
// This module and its contents are only compiled for doctests.
#[cfg(doctest)]
mod doctest_helpers {
// Re-export any types needed for the function signatures above.
pub use super::TestContext;
use std::io::Result;
pub struct TestContext {
//... fields for the test context...
}
pub fn setup_test_environment() -> Result<TestContext> {
// All the complex, shared setup logic lives here once.
println!("Setting up test environment...");
Ok(TestContext { /*... */ })
}
}
// A struct that might be needed by the public function signature.
// It can be defined normally.
pub struct TestContext { /*... */ }
```
This pattern is the most effective way to achieve DRY doctests. It centralizes
setup logic, improves maintainability, and cleanly separates testing concerns
from production code.[^11]
### 4.3 Advanced DRY: Programmatic Doctest Generation
For highly specialized use cases, such as authoring procedural macros, the DRY
principle can be taken a step further. A procedural macro generates code, and
it is often desirable to test that the generated code itself contains valid and
working documentation. Writing these doctests manually can be exceptionally
repetitive.
Crates like `quote-doctest` address this by allowing developers to
programmatically construct a doctest from a `TokenStream`. This enables the
generation of doctests from the same source of truth that generates the code
they are intended to test, representing the ultimate application of the DRY
principle in this domain.[^12]
## Conditional Compilation Strategies for Doctests
Conditional compilation is a powerful feature of Rust, but it introduces
significant complexity when interacting with `rustdoc`. A common source of
confusion stems from the failure to distinguish between two separate goals: (1)
ensuring platform-specific or feature-gated items *appear* in the
documentation, and (2) ensuring doctests for those items *execute* only under
the correct conditions. These two goals are achieved with different mechanisms
that operate at different stages of the `rustdoc` pipeline.
### 5.1 Documenting Conditionally Compiled Items: `#[cfg(doc)]`
**The Goal**: To ensure that an item, such as a `struct UnixSocket` that is
only available on Unix-like systems, is included in the documentation
regardless of which platform `rustdoc` is run on (e.g., when generating docs on
a Windows machine).
**The Mechanism**: `rustdoc` always invokes the compiler with the `--cfg doc`
flag set. By adding `doc` to an item's `#[cfg]` attribute, a developer can
instruct the compiler to include that item specifically for documentation
builds.[^13]
**The Pattern**:
```Rust
/// A socket that is only available on Unix platforms.
#[cfg(any(target_os = "unix", doc))]
pub struct UnixSocket;
```
This `any` directive ensures the struct is compiled either when the target OS
is `unix` OR when `rustdoc` is running. This correctly makes the item visible
in the generated HTML. However, it is crucial to understand that this **does
not** make the doctest for `UnixSocket` pass on non-Unix platforms.
This distinction highlights the "cfg duality." The `#[cfg(doc)]` attribute
controls the *table of contents* of the documentation; it determines which
items are parsed and rendered. The actual compilation of a doctest, however,
happens in a separate, later stage. In that stage, the `doc` cfg is *not*
passed to the compiler.[^13] The compiler only sees the host
`cfg` (e.g., `target_os = "windows"`), so the `UnixSocket` type is not
available, and the test fails to compile. `#[cfg(doc)]` affects what is
documented, not what is testable.
### 5.2 Executing Doctests Conditionally: Feature Flags
**The Goal**: To ensure a doctest that relies on an optional crate feature
(e.g., a feature named `"serde"`) is only executed when that feature is enabled
via `cargo test --doc --features "serde"`.
Two primary patterns exist to achieve this.
Pattern 1: #\[cfg\] Inside the Code Block
This pattern involves placing a #\[cfg\] attribute directly on the code within
the doctest itself.
```Rust
/// This example only runs if the "serde" feature is enabled.
///
/// ```
/// # #[cfg(feature = "serde")]
/// # {
/// # let my_struct = MyStruct::new();
/// # let json = serde_json::to_string(&my_struct).unwrap();
/// # assert_eq!(json, "{}");
/// # }
/// ```
```
When the `"serde"` feature is disabled, the code inside the block is compiled
out. The doctest becomes an empty program that runs, does nothing, and is
reported as `ok`. While simple to write, this can be misleading, as the test
suite reports a "pass" for a test that was effectively skipped.[^14]
Pattern 2: cfg_attr to Conditionally ignore the Test
A more explicit and accurate pattern uses the cfg_attr attribute to
conditionally add the ignore flag to the doctest's header. This is typically
done with inner doc comments (//!).
```Rust
//! #![cfg_attr(not(feature = "serde"), doc = "```ignore")]
//! #![cfg_attr(feature = "serde", doc = "```")]
//! // Example code that requires the "serde" feature.
//! let my_struct = MyStruct::new();
//! let json = serde_json::to_string(&my_struct).unwrap();
//! assert_eq!(json, "{}");
//! ```
```
With this pattern, if the `"serde"` feature is disabled, the test is marked as
`ignored` in the test results, which more accurately reflects its status. If
the feature is enabled, the `ignore` is omitted, and the test runs normally.
This approach provides clearer feedback but is significantly more verbose and
less ergonomic, especially when applied to outer (`///`) doc comments, as the
`cfg_attr` must be applied to every single line of the comment.[^14]
### 5.3 Displaying Feature Requirements in Docs: `#[doc(cfg(...))]`
To complement conditional execution, Rust provides a way to visually flag
feature-gated items in the generated documentation. This is achieved with the
`#[doc(cfg(...))]` attribute, which requires enabling the
`#![feature(doc_cfg)]` feature gate at the crate root.
```Rust
// At the crate root (lib.rs)
#![feature(doc_cfg)]
// On the feature-gated item
#[cfg(feature = "serde")]
#[doc(cfg(feature = "serde"))]
pub fn function_requiring_serde() { /*... */ }
```
This will render a banner in the documentation for `function_requiring_serde`
that reads, "This is only available when the `serde` feature is enabled." This
attribute is purely for documentation generation and is independent of, but
often used alongside, the conditional test execution patterns.[^14]
## Doctests in the Wider Project Ecosystem
Doctests are a powerful tool, but they are just one component of a
comprehensive testing strategy. Understanding their specific role and
limitations is key to maintaining a healthy and well-tested Rust project.
### 6.1 Choosing the Right Test Type: A Decision Framework
A robust testing strategy leverages three distinct types of tests, each with
its own purpose:
- **Doctests**: These are ideal for simple, "happy-path" examples of the
public API. Their dual purpose is to provide clear documentation for users
and to act as a basic sanity check that the examples remain correct over
time. They should be easy to read and focused on illustrating a single
concept.
- **Unit tests (`#[test]` in `src/`)**: These are for testing the
nitty-gritty details of the implementation. They are placed in submodules
within the source files (often `mod tests {... }`) and are compiled only with
`#[cfg(test)]`. Because they live inside the crate, they can access private
functions and modules, making them perfect for testing internal logic, edge
cases, and specific error conditions.
- **Integration Tests (in the** `tests/` **directory)**: These test the crate
from a completely external perspective, much like doctests. However, they are
not constrained by the need to be readable documentation. They are suited for
testing complex user workflows, interactions between multiple API entry
points, and the overall behaviour of the library as a black box.
### 6.2 The Unsolved Problem: Testing Private APIs
As established, the `rustdoc` compilation model makes testing private items in
doctests impossible by design.[^1] The community has developed several
workarounds, but each comes with significant trade-offs1. `ignore` **the test**: This allows the example to exist in the documentation
but sacrifices the guarantee of correctness. It is the least desirable
option.
2. **Make items** `pub` **in a** `detail` **or** `internal` **module**: This
compromises API design by polluting the public namespace and exposing
implementation details that should be encapsulated. It can lead to misuse by
users and makes future refactoring difficult.
3. **Use** `cfg_attr` **to conditionally make items public**: This involves
adding an attribute like
`#[cfg_attr(feature = "doctest-private", visibility::make(pub))]` to every
private item that requires testing. While robust, it is highly invasive and
adds significant boilerplate throughout the codebase.
The expert recommendation is to acknowledge this limitation and not fight the
tool. Do not compromise a clean API design for the sake of doctests. Use
doctests for their intended purpose—verifying public API examples—and rely on
dedicated unit tests for verifying private logic. The lack of a clean solution
for test-verifying private documentation is a known and accepted trade-off
within the Rust ecosystem.
### 6.3 Practical Challenges and Solutions
Beyond architectural considerations, developers face several practical,
real-world challenges when working with doctests.
- **The** `README.md` **Dilemma**: A project's `README.md` file serves multiple
audiences. It needs to render cleanly on platforms like GitHub and
[crates.io](http://crates.io), where hidden lines (`#...`) look like ugly,
commented-out code. At the same time, it should contain testable examples,
which often require hidden lines for setup. The best practice is to
avoid maintaining the README manually. Instead, use a tool like
`cargo-readme`. This tool generates a `README.md` file from the crate-level
documentation (in `lib.rs`), automatically stripping out the hidden lines.
This provides a single source of truth that is both fully testable via
`cargo test --doc` and produces a clean, professional README for external
sites.
- **Developer Ergonomics in IDEs**: Writing code inside documentation comments
can be a subpar experience. IDEs and tools like `rust-analyzer` often provide
limited or no autocompletion, real-time error checking, or refactoring
support for code within a comment block. A common and effective workflow
to mitigate this is to first write and debug the example as a standard
`#[test]` function in a temporary file or test module. This allows the
developer to leverage the full power of the IDE. Once the code is working
correctly, it can be copied into the doc comment, and the necessary
formatting (`///`, `#`, etc.) can be applied.
## Conclusion and Recommendations
Rust's documentation testing framework is a uniquely powerful feature that
promotes the creation of high-quality, reliable "living documentation." By
deeply understanding its underlying compilation model and the patterns that
have evolved to manage its constraints, developers can write doctests that are
effective, ergonomic, and maintainable. To summarize the key principles for
mastering doctests:
1. **Embrace the Model**: Treat a doctest as an external integration test
compiled in a separate crate; this mental model explains nearly all of its
behaviour.
2. **Prioritize Clarity**: Write examples that teach the *why*, not just the
*how*. Use hidden lines (`#`) ruthlessly to eliminate boilerplate and focus
the reader's attention on the relevant code.
3. **Handle Errors Gracefully**: For examples of fallible functions, always use
the `fn main() -> Result<...>` pattern, hiding the boilerplate. Avoid
`.unwrap()` to promote robust error-handling practices.
4. **Be DRY**: When setup logic is shared across multiple examples, centralize
it in a helper module guarded by `#[cfg(doctest)]` to avoid repetition.
5. **Master** `cfg`: Use `#[cfg(doc)]` to control an item's *visibility* in the
final documentation. Use `#[cfg(feature = "...")]` or other `cfg` flags
*inside* the test block to control its conditional *execution*. Do not
confuse the two.
6. **Know When to Stop**: A doctest is not the right tool for every job. When
an example becomes overly complex, requires testing intricate error paths,
or needs to access private implementation details, move it to a dedicated
unit or integration test. Do not compromise your API design or test clarity
by forcing a square peg into a round hole. Use the right tool for the job.
### **Works cited**
accessed on July 15, 2025,
<https://stackoverflow.com/questions/70111757/how-can-i-write-documentation-tests-for-private-modules>
<https://swatinem.de/blog/fix-rustdoc/>
July 15, 2025, <https://doc.rust-lang.org/rustdoc/documentation-tests.html>
<https://ebarnard.github.io/2019-06-03-rust-smaller-trait-implementers-docs/rustdoc/documentation-tests.html>
<http://web.mit.edu/rust-lang_v1.25/arch/amd64_ubuntu1404/share/doc/rust/html/rustdoc/documentation-tests.html>
2025, <https://blog.logrocket.com/how-to-organize-rust-tests/>
<https://www.reddit.com/r/rust/comments/qk77iu/best_way_to_organise_tests_in_rust/>
<https://dev.to/gritmax/writing-rust-documentation-5hn5>
<https://doc.rust-lang.org/rustdoc/what-is-rustdoc.html>
<https://rust-lang.github.io/api-guidelines/documentation.html>
Language Forum, accessed on July 15, 2025,
<https://users.rust-lang.org/t/compile-fail-doc-test-ignored-in-cfg-test/124927>
Test setup for doctests – help – The Rust Programming Language Forum, accessed
on July 15, 2025, <https://users.rust-lang.org/t/test-setup-for-doctests/50426>
2025, <https://docs.rs/quote-doctest>
accessed on July 15, 2025,
<https://stackoverflow.com/questions/50312190/how-can-i-conditionally-execute-a-module-level-doctest-based-on-a-feature-flag>
have doctests?, accessed on July 15, 2025,
<https://stackoverflow.com/questions/38292741/how-would-one-achieve-conditional-compilation-with-rust-projects-that-have-docte>
2025,
<https://www.reddit.com/r/rust/comments/ke438a/how_do_you_write_your_doc_tests/>