docs: documentation patterns from rust-lang/rust
10 patterns, 723 lines. Full spec compliance. Patterns: doc comments, # Examples, # Safety, # Panics, # Errors, intra-doc links, doc test attributes, module docs, // SAFETY:, #[doc(hidden)].
This commit is contained in:
@@ -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<T>` to `Option<&T>`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// Calculates the length of an <code>Option<[String]></code> as an
|
||||||
|
/// <code>Option<[usize]></code> 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<String> = Some("Hello, world!".to_string());
|
||||||
|
/// let text_length: Option<usize> = 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<u8> {
|
||||||
|
// 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<u8>` 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<u8> { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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<u32> = 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<I>(&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<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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<T, E>`
|
||||||
|
- 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<Connection, ConnectError> { ... }
|
||||||
|
// 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<Connection, ConnectError> { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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
|
||||||
|
|
||||||
|
<!-- PATTERN_COMPLETE -->
|
||||||
Reference in New Issue
Block a user