Skill 13 · Rust Best Practices
Subchapter 13.8
references/chapter_08.mdMarkdown10 KBView on GitHub
Clear code beats clear comments. However, when the why isn’t obvious, comment it plainly - or link to where you can read more context.
| Purpose | Use // comment | Use /// doc or //! crate doc |
|---|---|---|
| Describe Why | ✅ Yes - explains tricky reasoning | ❌ Not for documentation |
| Describe API | ❌ Not useful | ✅ Yes - public interfaces, usage, details, errors, panics |
| Maintainable | 🚨 Often becomes obsolete and hard to reason | ✅ Tied to code, appears in generated docs and can run test cases |
| Visibility | Local development only | Exported to users and tools like cargo doc |
Use // comments (double slashed) when something can’t be expressed clearly in code, like:
#[cfg(..)].Name your comments! For example, a comment regarding a safety guarantee should start with
// SAFETY: ....
// SAFETY: `ptr` is guaranteed to be non-null and aligned by caller
unsafe { std::ptr::copy_nonoverlapping(src, dst, len); }// CONTEXT: Reuse root cert store across subgraphs to avoid duplicate OS calls:
// [ADR-12](link/to/adr-12): TLS Performance on MacOSAvoid comments that:
// increment i by 1 for the next loop).TODOs without actions (links to some versioned issue).fn compute(counter: &mut usize) {
// increment by 1
*counter += 1;
}// Originally written in 2028 for some now-defunct platformComments as a “living documentation” is a dangerous myth, as comments are not free:
If something deserves to live beyond a PR, put it in:
Instead of long commented blocks, break logic into named helper functions:
fn save_user(&self) -> Result<(), MyError> {
// check if the user is authenticated
if self.is_authenticated() {
// serialize user data
let data = serde_json::to_string(self)?;
// write to file
std::fs::write(self.path(), data)?;
}
}✅ Extract for clarity:
fn save_auth_user(&self) -> Result<PathBuf, MyError> {
if self.is_authenticated() {
let path = self.path();
let serialized_user = serde_json::to_string(self)?;
std::fs::write(path, serialized_user)?;
Ok(path)
} else {
Err(MyError::UserNotAuthenticated)
}
}❗ Extract when the name adds meaning – not to deduplicate a couple of similar-looking lines. See Chapter 1, §1.8 for when duplication is the better trade-off.
Don’t leave // TODO: scattered around the codebase with no owner. Instead:
// TODO(issue #42): Remove workaround after bugfixThis makes TODOs trackable, actionable and visible to everyone.
Use /// doc comments to document:
Errors and Panics./// Loads [`User`] profile from disk
///
/// # Error
/// - Returns [`MyError`] if the file is missing [`MyError::FileNotFound`].
/// - Returns [`MyError`] if the content is an invalid Json, [`MyError::InvalidJson`].
fn load_user(path: &Path) -> Result<User, MyError> {...}Doc comments can also include examples, links and even tests:
/// Returns the square of the integer part of any number.
/// Square is limited to `u128`.
///
/// # Examples
///
/// ```rust
/// assert_eq!(square(4.3), 16)
/// ```
fn square(x: impl ToInt) -> u128 { ... }Rust provides first-class documentation tooling via rustdoc, which makes documenting your code a key part of writing idiomatic and maintainable rust. There are doc specific lints to help with documentation, like:
| Lint | Description |
|---|---|
| missing_docs (opens in a new tab) | Warns that a public functions, struct, const, enum has missing documentation |
| broken_intra_doc_links (opens in a new tab) | Detects if an internal documentation link is broken. Specially useful when things are renamed. |
| empty_docs (opens in a new tab) | Disallow empty docs - preventing bypass of missing_docs |
| missing_panics_doc (opens in a new tab) | Warns that documentation should have a # Panics section if function can panic |
| missing_errors_doc (opens in a new tab) | Warns that documentation should have a # Errors section if function returns a Result explaining Err conditions |
| missing_safety_doc (opens in a new tab) | Warns that documentation should have a # Safety section if public facing functions have visible unsafe blocks |
| Style | Used for | Scope | Example |
|---|---|---|---|
/// | Line doc comment | Public items like struct, fn, enum, consts | Documenting, giving context and usage to fn, struct, enum, etc |
//! | Module level doc comment | Modules or entire crates | Explaining crate/module purpose with common use cases and quickstart |
Use /// for functions, structs, traits, enums, const, etc:
/// Adds two numbers together.
///
/// # Examples
///
/// ```
/// let result = my_crate::add(2, 3);
/// assert_eq!(result, 5);
/// ```
pub fn add(a: i32, b: i32) -> i32 {
a + b
}# Examples section to better explain how to use it.cargo test, even if you have to hide their output with starting #:/// ```
/// let result = my_crate::add(2, 3);
/// # assert_eq!(result, 5);
/// ```# Panics, # Errors and # Safety sections when relevant.Use //! when you want to document the purpose of a module or a crate. It is places at the top of a lib.rs or mod.rs file, for example engine/mod.rs:
//! This module implements a custom chess engine.
//!
//! It handles board state, move generation and check detection.
//!
//! # Example
//! ```
//! let board = chess::engine::Board::default();
//! assert!(board.is_valid());
//! ```📦 Crate-Level (lib.rs)
//! doc at top explains what the crate does, and what problems it solves.# Examples or pointers to modules.📁 Modules (mod.rs or inline)
//! doc explains what this module is for, its exports, and invariants.🧱 Structs, Enums, Traits
/// doc explains:
#[non_exhaustive] (opens in a new tab) if external users may match on it.🔧 Functions and Methods
/// doc covers:
# Panics, # Errors).# Examples.📑 Traits
📦 Public Constants
cargo doc --open to check your output often.#![deny(missing_docs)] and other relevant doc lints in top-level modules if you want to enforce full doc coverage.