diff --git a/patterns/documentation.md b/patterns/documentation.md new file mode 100644 index 0000000..d4eb604 --- /dev/null +++ b/patterns/documentation.md @@ -0,0 +1,723 @@ +# 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 + +