Documentation

Writing High-Quality API Documentation

High-quality Rust API documentation follows the conventions established by the Rust standard library and the official Rust API Guidelines (C-EXAMPLE, C-FAILURE, C-LINK): every public module, type, and function opens with a concise third-person summary line, explains edge cases and invariants without repeating the type signature, and links related concepts into a cohesive navigation graph.

Anatomy of a Complete Rust Doc Comment:

1. Summary Line:   Single sentence in 3rd-person present tense ("Returns...", "Parses...")
                   (Must fit cleanly in module index tables!)
2. Body Prose:     When to use this item, behavior on edge cases, complexity, invariants
3. # Errors:       Exact conditions returning `Err(E)` (with intra-doc links to variants)
4. # Panics:       Exact conditions causing a panic (with link to non-panicking `try_*`)
5. # Safety:       Exact preconditions required by `unsafe fn` / `unsafe trait`
6. # Examples:     Runnable, focused code block verified by `cargo test --doc`

Rust API Documentation Style Guidelines

Guideline Avoid (Anti-Pattern) Prefer (Standard Rust Style)
Opening verb tense "Return the length" (imperative) or "This function returns..." (wordy) "Returns the number of elements in the buffer." (third-person indicative)
Summary paragraph length A 5-sentence wall of text in the first paragraph A single concise sentence, followed by a blank /// line before details
Restating signatures "Takes a &str called s and returns a Result<u32, ParseIntError>." Explain meaning and units ("Parses a timeout duration in milliseconds.")
Cross-referencing Unlinked plain text "See RingBuffer::pop" Clickable intra-doc link "See [`RingBuffer::pop`]."
Units & Ranges "Sets the timeout" "Sets the read timeout in milliseconds (1..=60_000)."

Complete Example: A Documented Module Following All Guidelines

#![warn(missing_docs)]
#![warn(clippy::missing_errors_doc)]
#![warn(clippy::missing_panics_doc)]
#![deny(rustdoc::broken_intra_doc_links)]

//! Fixed-capacity ring buffer for bounded telemetry queues.
//!
//! Use [`RingBuffer`] when you need $O(1)$ FIFO insertion and removal with a strict
//! upper bound on allocated items.

use std::collections::VecDeque;

/// Error returned when pushing into a full [`RingBuffer`].
#[derive(Debug, PartialEq, Eq)]
pub struct FullError<T>(
    /// The item that could not be inserted because the buffer was at capacity.
    pub T,
);

/// A bounded FIFO queue backed by a pre-allocated [`VecDeque`].
///
/// # Examples
///
/// ```
/// # use my_crate::{RingBuffer, FullError};
/// let mut ring = RingBuffer::with_capacity(2);
/// ring.try_push(10).unwrap();
/// ring.try_push(20).unwrap();
///
/// // Pushing beyond capacity returns `FullError` with the rejected item:
/// assert_eq!(ring.try_push(30), Err(FullError(30)));
/// assert_eq!(ring.pop(), Some(10));
/// ```
#[derive(Debug)]
pub struct RingBuffer<T> {
    buf: VecDeque<T>,
    capacity: usize,
}

impl<T> RingBuffer<T> {
    /// Creates an empty [`RingBuffer`] capable of holding up to `capacity` elements.
    ///
    /// # Panics
    ///
    /// Panics if `capacity == 0`.
    #[track_caller]
    pub fn with_capacity(capacity: usize) -> Self {
        assert!(capacity > 0, "RingBuffer capacity must be greater than 0");
        Self {
            buf: VecDeque::with_capacity(capacity),
            capacity,
        }
    }

    /// Appends `item` to the back of the buffer if space is available.
    ///
    /// Runs in $O(1)$ time and never reallocates memory.
    ///
    /// # Errors
    ///
    /// Returns [`Err`]`(`[`FullError`]`(item))` if `self.len() == self.capacity()`,
    /// returning ownership of `item` back to the caller.
    pub fn try_push(&mut self, item: T) -> Result<(), FullError<T>> {
        if self.buf.len() == self.capacity {
            return Err(FullError(item));
        }
        self.buf.push_back(item);
        Ok(())
    }

    /// Removes and returns the oldest element from the front of the buffer,
    /// or [`None`] if the buffer is empty.
    pub fn pop(&mut self) -> Option<T> {
        self.buf.pop_front()
    }

    /// Returns the number of elements currently stored in the buffer.
    pub fn len(&self) -> usize {
        self.buf.len()
    }

    /// Returns `true` if the buffer contains no elements.
    pub fn is_empty(&self) -> bool {
        self.buf.is_empty()
    }

    /// Returns the maximum number of elements the buffer can hold.
    pub fn capacity(&self) -> usize {
        self.capacity
    }
}

fn main() {
    let mut ring = RingBuffer::with_capacity(2);
    ring.try_push("event-1").unwrap();
    ring.try_push("event-2").unwrap();
    println!(
        "Ring len={}/{}, popped={:?}",
        ring.len(),
        ring.capacity(),
        ring.pop()
    );
}
Ring len=2/2, popped=Some("event-1")