# Rust Documentation Patterns Patterns for documentation in Rust, extracted from the standard library source. **Source:** [rust-lang/rust](https://github.com/rust-lang/rust) at commit [`f53b654`](https://github.com/rust-lang/rust/tree/f53b654a8882fd5fc036c4ca7a4ff41ce32497a6) **Stats:** 133,741 doc comments, 3,912 `# Examples` sections, 2,864 `# Safety` sections, 442 `# Panics` sections, 139 `# Errors` sections, 144 doc tests in option.rs alone. --- ## 1. Doc Comments (///) on Every Public Item ### Source: [library/core/src/option.rs](https://github.com/rust-lang/rust/blob/f53b654a8882fd5fc036c4ca7a4ff41ce32497a6/library/core/src/option.rs) Every public function, struct, enum, trait, and module in the stdlib has a `///` doc comment. 133,741 total across library/. ```rust // library/core/src/option.rs /// Converts from `&Option` to `Option<&T>`. /// /// # Examples /// /// Calculates the length of an Option<[String]> as an /// Option<[usize]> without moving the [`String`]. /// The [`map`] method takes the `self` argument by value, /// consuming the original, so this technique uses `as_ref` to first /// take an `Option` to a reference to the value inside the original. /// /// [`map`]: Option::map /// /// ``` /// let text: Option = Some("Hello, world!".to_string()); /// let text_length: Option = text.as_ref().map(|s| s.len()); /// println!("still can print text: {text:?}"); /// ``` pub const fn as_ref(&self) -> Option<&T> { ... } ``` ### Why Doc comments are compiled, tested, and rendered as HTML. They're not afterthoughts — they're part of the type system's usability. `rustdoc` generates browsable documentation from these. Doc tests are real tests that run in CI. ### When to Use **Triggers:** - Item is `pub` (visible to users) - ANY public API — functions, structs, enums, traits, modules, constants **Example — before:** ```rust pub fn encode(data: &[u8], key: &[u8]) -> Vec { // XOR encode data.iter().zip(key.iter().cycle()).map(|(a, b)| a ^ b).collect() } // No one knows what this does, what key format, what happens with empty input ``` **Example — after:** ```rust /// XOR-encodes `data` using `key`, cycling the key as needed. /// /// Returns a new `Vec` of the same length as `data`. /// Encoding is symmetric: encoding the output with the same key /// returns the original data. /// /// # Examples /// /// ``` /// let encoded = encode(b"hello", b"key"); /// let decoded = encode(&encoded, b"key"); /// assert_eq!(decoded, b"hello"); /// ``` /// /// # Panics /// /// Panics if `key` is empty. pub fn encode(data: &[u8], key: &[u8]) -> Vec { ... } ``` ### When NOT to Use **Don't use this when:** - Item is private (use `//` regular comments for internal docs) - The function is a trait impl where the trait already documents behavior (use `/// See [`Trait::method`].` or nothing) --- ## 2. # Examples Section (Executable Doc Tests) ### Source: 3,912 `# Examples` sections in library/. option.rs has 144 doc code blocks, result.rs has 102. ```rust /// # Examples /// /// ``` /// let x: Option = Some(2); /// assert_eq!(x.unwrap(), 2); /// ``` ``` ### Why Doc examples are compiled and run as tests (`cargo test` includes doc tests). This means: 1. Examples are always correct (they compile and pass) 2. Breaking changes break doc tests (forces updates) 3. Users get copy-pasteable working code ### When to Use **Triggers:** - Every public function should have at least one example - Complex APIs need multiple examples showing different use cases - The example should demonstrate the "happy path" first **Example — before:** ```rust /// Splits the string at the given delimiter. pub fn split_at(&self, mid: usize) -> (&str, &str) { ... } // User: "What happens at boundary? What if mid > len?" ``` **Example — after:** ```rust /// Splits the string at byte position `mid`. /// /// # Examples /// /// Basic usage: /// /// ``` /// let s = "Hello, world!"; /// let (first, second) = s.split_at(7); /// assert_eq!(first, "Hello, "); /// assert_eq!(second, "world!"); /// ``` /// /// Splitting at the beginning and end: /// /// ``` /// let s = "hello"; /// assert_eq!(s.split_at(0), ("", "hello")); /// assert_eq!(s.split_at(5), ("hello", "")); /// ``` pub fn split_at(&self, mid: usize) -> (&str, &str) { ... } ``` ### When NOT to Use **Don't use this when:** - The function is trivially obvious (getter with no edge cases) - The example would require complex setup that distracts from the point (use `# ` hidden lines or `no_run` instead) --- ## 3. # Safety Section (Unsafe Contract) ### Source: [library/core/src/slice/mod.rs](https://github.com/rust-lang/rust/blob/f53b654a8882fd5fc036c4ca7a4ff41ce32497a6/library/core/src/slice/mod.rs) (get_unchecked) 2,864 `# Safety` sections in library/. ```rust /// # Safety /// /// Calling this method with an out-of-bounds index is /// *[undefined behavior]* even if the resulting reference is not used. /// /// You can think of this like `.get(index).unwrap_unchecked()`. /// It's UB to call `.get_unchecked(len)`, even if you immediately /// convert to a pointer. /// /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html /// /// # Examples /// /// ``` /// let x = &[1, 2, 4]; /// /// unsafe { /// assert_eq!(x.get_unchecked(1), &2); /// } /// ``` pub unsafe fn get_unchecked(&self, index: I) -> &I::Output { ... } ``` ### Why Every `unsafe fn` MUST document what the caller must guarantee. This is a contract: "I (the implementor) promise correctness IF you (the caller) uphold these invariants." Without this, unsafe code is unauditable. ### When to Use **Triggers:** - The function is `unsafe fn` - The function has an `unsafe` block with non-obvious invariants - A trait has unsafe methods that implementors must uphold **Example — before:** ```rust /// Gets element without bounds checking. pub unsafe fn get_fast(&self, index: usize) -> &T { ... } // Caller: "What are the actual requirements? Just index < len?" ``` **Example — after:** ```rust /// Returns a reference to the element at `index` without bounds checking. /// /// # Safety /// /// The caller must ensure: /// - `index < self.len()` /// - The returned reference must not outlive the slice /// - No mutable reference to the same element exists /// /// Violating any of these is undefined behavior. /// /// # Examples /// /// ``` /// let data = &[10, 20, 30]; /// unsafe { /// assert_eq!(*data.get_fast(1), 20); /// } /// ``` pub unsafe fn get_fast(&self, index: usize) -> &T { ... } ``` ### When NOT to Use **Don't use this when:** - The function is safe (no `unsafe` keyword) - The safety requirements are documented on the trait, not repeated on every method --- ## 4. # Panics Section ### Source: 442 `# Panics` sections in library/. ```rust /// # Panics /// /// Panics if `index >= self.len()`. pub fn swap(&mut self, a: usize, b: usize) { ... } ``` ### Why If a function can panic, users MUST know when. Panics are not errors you can catch (in normal code) — they abort the thread. Documenting them lets callers write defensive code. ### When to Use **Triggers:** - Function uses `unwrap()`, `expect()`, `panic!()`, or `assert!()` - Function indexes into a collection - Any input combination causes a panic **Example — before:** ```rust /// Divides a by b. pub fn divide(a: f64, b: f64) -> f64 { assert!(b != 0.0, "division by zero"); a / b } // User finds out about the panic the hard way ``` **Example — after:** ```rust /// Divides `a` by `b`. /// /// # Panics /// /// Panics if `b` is zero. /// /// # Examples /// /// ``` /// assert_eq!(divide(10.0, 2.0), 5.0); /// ``` /// /// ```should_panic /// divide(1.0, 0.0); // panics /// ``` pub fn divide(a: f64, b: f64) -> f64 { ... } ``` ### When NOT to Use **Don't use this when:** - The function never panics - Panics are impossible given the type constraints (but document the proof in a comment) --- ## 5. # Errors Section ### Source: 139 `# Errors` sections in library/. ```rust /// # Errors /// /// This function will return an error if `path` does not already exist. /// Other errors may also be returned according to [`OpenOptions::open`]. pub fn read>(path: P) -> io::Result> { ... } ``` ### Why When a function returns `Result`, document WHICH errors can occur and WHEN. This lets callers decide how to handle each case without reading the implementation. ### When to Use **Triggers:** - Function returns `Result` - The error conditions aren't obvious from the signature - Different error variants have different recovery strategies **Example — before:** ```rust /// Connects to the server. pub fn connect(addr: &str) -> Result { ... } // Which ConnectError variants? When does each happen? ``` **Example — after:** ```rust /// Connects to the server at `addr`. /// /// # Errors /// /// Returns [`ConnectError::InvalidAddress`] if `addr` cannot be parsed. /// Returns [`ConnectError::Timeout`] if connection is not established /// within 30 seconds. /// Returns [`ConnectError::Refused`] if the server actively refuses /// the connection. pub fn connect(addr: &str) -> Result { ... } ``` ### When NOT to Use **Don't use this when:** - The error type is self-documenting (`io::Error` with standard kinds) - There's only one possible error and it's obvious from the name --- ## 6. Intra-Doc Links ([`Type::method`]) ### Source: Thousands of intra-doc links across the stdlib. ```rust /// See [`Option::map`] for the `Option` equivalent. /// Unlike [`Result::unwrap`], this does not panic. /// Returns the contained [`Ok`] value, consuming the `self` value. ``` ### Why Intra-doc links are resolved by rustdoc into clickable hyperlinks. They're checked at compile time — broken links are warnings. This is how you create navigable documentation. ### When to Use **Triggers:** - Referencing another type, method, or trait - Cross-referencing related functionality - "See also" references **Example — before:** ```rust /// Similar to unwrap but returns a default value instead of panicking. /// The map function can transform the value before unwrapping. pub fn unwrap_or_default(self) -> T { ... } ``` **Example — after:** ```rust /// Returns the contained [`Some`] value or a [`Default`] value. /// /// Consumes `self`, then if [`Some`], returns the contained value, /// otherwise if [`None`], returns the [default value] for `T`. /// /// See [`unwrap_or`] for a version with a custom fallback. /// See [`map`] to transform the contained value. /// /// [`unwrap_or`]: Option::unwrap_or /// [`map`]: Option::map /// [default value]: Default::default pub fn unwrap_or_default(self) -> T where T: Default { ... } ``` ### When NOT to Use **Don't use this when:** - The reference is to an external crate (use full URLs) - The link would be more confusing than helpful - You're referencing something in a code block (code blocks aren't linked) --- ## 7. Doc Test Attributes (no_run, should_panic, compile_fail) ### Source: Multiple test annotations used across the stdlib: ```rust /// ```should_panic /// vec![1, 2, 3][99]; // panics at runtime /// ``` /// ```compile_fail /// let x: &str = 42; // type error /// ``` /// ```no_run /// loop { /* runs forever */ } /// ``` /// ```ignore /// // Platform-specific, can't test in CI /// ``` ``` ### Why Not all examples should run. `should_panic` proves the panic docs. `compile_fail` proves the type system rejects invalid code. `no_run` compiles but doesn't execute (for I/O, network, infinite loops). `ignore` skips entirely (last resort). ### When to Use **Triggers:** - `should_panic`: Demonstrating that invalid input causes a panic - `compile_fail`: Showing that the type system prevents misuse - `no_run`: Example requires network, file system, or blocks forever - `ignore`: Platform-specific or requires external setup **Example — before:** ```rust /// # Panics /// Panics if index is out of bounds. pub fn get(&self, index: usize) -> &T { ... } // How do users know the panic really happens? ``` **Example — after:** ```rust /// # Panics /// /// Panics if `index >= self.len()`. /// /// ```should_panic /// let v = vec![1, 2, 3]; /// v[99]; // panics: index out of bounds /// ``` pub fn get(&self, index: usize) -> &T { ... } ``` ### When NOT to Use **Don't use this when:** - The example can run normally (just use ```) - You're using `ignore` to hide broken examples (fix them instead) --- ## 8. Module-Level Documentation (//!) ### Source: [library/core/src/option.rs](https://github.com/rust-lang/rust/blob/f53b654a8882fd5fc036c4ca7a4ff41ce32497a6/library/core/src/option.rs) (module doc) ```rust //! Optional values. //! //! Type [`Option`] represents an optional value: every [`Option`] //! is either [`Some`] and contains a value, or [`None`], and //! does not. [`Option`] types are very common in Rust code, as //! they have a number of uses: //! //! * Initial values //! * Return values for functions that are not defined //! over their entire input range (partial functions) //! * ... ``` ### Why Module docs (`//!`) explain the module's purpose, key types, and common patterns. They appear at the top of the rustdoc page. This is where you explain WHY the module exists, not just WHAT it contains. ### When to Use **Triggers:** - Every `mod.rs` or `lib.rs` file - Any module with public exports - When the module's purpose isn't obvious from its name **Example — before:** ```rust // src/cache/mod.rs pub mod lru; pub mod ttl; pub mod eviction; // No explanation of how these relate or which to choose ``` **Example — after:** ```rust //! In-memory caching with configurable eviction strategies. //! //! This module provides three cache implementations: //! //! - [`lru::Cache`] — Least Recently Used eviction. Best for //! access-pattern-driven workloads. //! - [`ttl::Cache`] — Time-To-Live eviction. Best for data //! that becomes stale after a fixed duration. //! - [`eviction::Policy`] — Trait for custom eviction strategies. //! //! # Quick Start //! //! ``` //! use cache::lru::Cache; //! let mut c = Cache::new(100); //! c.insert("key", "value"); //! ``` pub mod lru; pub mod ttl; pub mod eviction; ``` ### When NOT to Use **Don't use this when:** - The module contains a single type and its name says it all - The module is private/internal --- ## 9. // SAFETY: Comments on Unsafe Blocks ### Source: 2,463 `// SAFETY:` comments in library/. ```rust // library/core/src/slice/mod.rs unsafe { // SAFETY: the caller must uphold the safety contract for `get_unchecked`. &*self.as_ptr().add(index) } ``` ### Why Every `unsafe { }` block must explain WHY it's sound. The comment justifies that the invariants required by the unsafe operation are upheld at this specific call site. This is how you audit unsafe code. ### When to Use **Triggers:** - Every `unsafe { }` block (no exceptions in the stdlib) - The comment explains the specific proof of soundness HERE - Not just "we checked" but "because X guarantees Y which means Z" **Example — before:** ```rust unsafe { ptr::copy_nonoverlapping(src, dst, count); } // Reviewer: "Is this actually safe? Who knows?" ``` **Example — after:** ```rust // SAFETY: `src` and `dst` are both derived from the same allocation // and are non-overlapping because `src` points to [0..mid] and `dst` // points to [mid..len]. `count` is bounded by `len - mid` which we // checked above with the assert. unsafe { ptr::copy_nonoverlapping(src, dst, count); } ``` ### When NOT to Use **Don't use this when:** - There's no unsafe block - The safety is already explained by a `# Safety` doc comment on the enclosing function (but STILL add the inline comment) --- ## 10. #[doc(hidden)] for Internal API ### Source: [library/std/src/lib.rs](https://github.com/rust-lang/rust/blob/f53b654a8882fd5fc036c4ca7a4ff41ce32497a6/library/std/src/lib.rs) ```rust #[doc(hidden)] #[unstable(feature = "std_internals", issue = "none")] pub mod macros; ``` ### Why `#[doc(hidden)]` keeps items out of the generated documentation. The item is still public (needed for macro expansion or cross-crate use) but not part of the documented API. Users shouldn't depend on it. ### When to Use **Triggers:** - Item must be `pub` for technical reasons (macros, codegen) but isn't meant for users - Implementation details exposed by necessity - Deprecated items you want to hide but not remove yet **Example — before:** ```rust // This is public but users shouldn't use it pub fn __internal_realloc(ptr: *mut u8, size: usize) -> *mut u8 { ... } // Shows up in rustdoc — confuses users ``` **Example — after:** ```rust /// Internal reallocation helper. Not part of the public API. #[doc(hidden)] pub fn __internal_realloc(ptr: *mut u8, size: usize) -> *mut u8 { ... } // Hidden from rustdoc but still compilable by macros that need it ``` ### When NOT to Use **Don't use this when:** - You can make the item `pub(crate)` instead (preferred) - The item genuinely should be documented for users - You're hiding items to avoid documenting them (lazy) --- ## Summary: Documentation Decision Tree ``` Is the item public? ├── NO → Regular comments (//) for complex logic only └── YES → Doc comment (///) required ├── What sections to include? │ ├── Always: Summary line + # Examples │ ├── If can panic: # Panics │ ├── If returns Result: # Errors │ ├── If unsafe: # Safety │ └── If related items exist: intra-doc links ├── Is it a module? │ └── Use //! at the top (module-level docs) └── Doc test attributes? ├── Normal example: ``` ├── Shows a panic: ```should_panic ├── Shows type error: ```compile_fail ├── Can't run (I/O): ```no_run └── Last resort: ```ignore ``` | Section | When | Example Header | |---|---|---| | Summary | Always | First line of `///` | | `# Examples` | Always (public API) | `/// # Examples` | | `# Safety` | `unsafe fn` or `unsafe trait` | `/// # Safety` | | `# Panics` | Function can panic | `/// # Panics` | | `# Errors` | Returns `Result` | `/// # Errors` | | `// SAFETY:` | Inside `unsafe {}` block | `// SAFETY: ...` | | `#[doc(hidden)]` | Public but internal | Attribute | | `//!` | Module/crate docs | Inner doc comment | See also: - [error-handling.md](error-handling.md) — Error types and Result docs - [unsafe-patterns.md](unsafe-patterns.md) — Safety contracts - [api-design.md](api-design.md) — What to document publicly