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