All skills
apollographql avatar

/rust-best-practices

@13ff457 official
by Apollo GraphQLapollographql/skills115 stars
13

Guide for writing idiomatic Rust code based on Apollo GraphQL's best practices handbook. Use this skill when: (1) writing new Rust code or functions, (2) reviewing or refactoring existing Rust code, (3) deciding between borrowing vs cloning or ownership patterns, (4) implementing error handling with Result types, (5) optimizing Rust code for performance, (6) writing tests or documentation for Rust projects.

Use this Skill: https://skilld.dev/gh/apollographql/skills/rust-best-practices

This session only. Nothing lands on disk.

referenceschapter_08.md

≈2.7k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Chapter 8 - Comments vs Documentation

Clear code beats clear comments. However, when the why isn't obvious, comment it plainly - or link to where you can read more context.

8.1 Comments vs Documentation: Know the Difference

| 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 |

8.2 When to use comments

Use // comments (double slashed) when something can't be expressed clearly in code, like:

  • Safety Guarantees, some of which can be better expressed with code conditionals.
  • Workarounds or Optimizations.
  • Legacy or platform-specific behaviors. Some of them can be expressed with #[cfg(..)].
  • Links to Design Docs or ADRs.
  • Assumptions or gotchas that aren't obvious.

Name your comments! For example, a comment regarding a safety guarantee should start with // SAFETY: ....

✅ Good comment:

// SAFETY: `ptr` is guaranteed to be non-null and aligned by caller
unsafe { std::ptr::copy_nonoverlapping(src, dst, len); }

✅ Design context comment:

// CONTEXT: Reuse root cert store across subgraphs to avoid duplicate OS calls:
// [ADR-12](link/to/adr-12): TLS Performance on MacOS

8.3 When comments get in the way

Avoid comments that:

  • Restate obvious things (// increment i by 1 for the next loop).
  • Can grow stale over time.
  • TODOs without actions (links to some versioned issue).
  • Could be replaced by better naming or smaller functions.

❌ Bad comment:

fn compute(counter: &mut usize) {
    // increment by 1
    *counter += 1;
}

❌ Too long or outdated

// Originally written in 2028 for some now-defunct platform

8.4 Don't Write Living Documentation (living comments)

Comments as a "living documentation" is a dangerous myth, as comments are not free:

  • They rot - nobody compiles comments.
  • They mislead - readers usually assume they are true with no critique, e.g. "the other developer knows this code better than I do".
  • They go stale - unless maintained with the code, they become irrelevant.
  • They are noisy - comments can clutter your code with multiple unnecessary lines.

If something deserves to live beyond a PR, put it in:

  • An ADR (Architectural Design Record).
  • A Design Document.
  • Document it in code by using types, doc comments, examples, renaming code blocks into cleaner functions.
  • Add tests to cover and explain the change.

🚨 If you find a comment, read it in context. Does it still make sense? If not, remove or update it, or ask for help. Comments should bother you.

8.5 Replace Comments with Code

Instead of long commented blocks, break logic into named helper functions:

❌ Commented code block:
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.

8.6 TODO should become issues

Don't leave // TODO: scattered around the codebase with no owner. Instead:

  1. File Github Issue or Jira Ticket. (Prefer github issues on public repositories).
  2. Reference the issue in the code:
// TODO(issue #42): Remove workaround after bugfix

This makes TODOs trackable, actionable and visible to everyone.

8.7 When to use doc comments

Use /// doc comments to document:

  • All public functions, structs, traits, enums.
  • Their purpose, their usage and their behaviors.
  • Anything developers need to understand how to use it correctly.
  • Add context that related to Errors and Panics.
  • Plenty of examples.

✅ Good doc comment:

/// 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 { ... }

8.8 Documentation in Rust: How, When and Why

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 | Warns that a public functions, struct, const, enum has missing documentation | | broken_intra_doc_links | Detects if an internal documentation link is broken. Especially useful when things are renamed. | | empty_docs | Disallow empty docs - preventing bypass of missing_docs | | missing_panics_doc | Warns that documentation should have a # Panics section if function can panic | | missing_errors_doc | Warns that documentation should have a # Errors section if function returns a Result explaining Err conditions | | missing_safety_doc | Warns that documentation should have a # Safety section if public facing functions have visible unsafe blocks |

Difference between /// and //!

| 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 |

/// Item level documentation

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
}
  • ✅ Write clear and descriptive What it does and how to use it.
  • ✅ Use # Examples section to better explain how to use it.
  • ✅ Prefer writing examples that can be tested via cargo test, even if you have to hide their output with starting #:
/// ```
/// let result = my_crate::add(2, 3);
/// # assert_eq!(result, 5);
/// ```
  • ✅ Use # Panics, # Errors and # Safety sections when relevant.
  • Add relevant context to the type.

//! Module/Crate level Documentation

Use //! when you want to document the purpose of a module or a crate. It is placed 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());
//! ```

8.9 Checklist for Documentation coverage

📦 Crate-Level (lib.rs)

  • //! doc at top explains what the crate does, and what problems it solves.
  • Includes crate-level # Examples or pointers to modules. 📁 Modules (mod.rs or inline)
  • //! doc explains what this module is for, its exports, and invariants.
  • Avoid repeating doc comments on re-exported items unless clarification is needed. 🧱 Structs, Enums, Traits
  • /// doc explains:
    • The role this type plays.
    • Invariants or expectations.
    • Example construction or usage.
  • Consider using #[non_exhaustive] if external users may match on it. 🔧 Functions and Methods
  • /// doc covers:
    • What it does.
    • Parameters and their meaning.
    • Return value behavior.
    • Edge cases (# Panics, # Errors).
    • Usage example, # Examples. 📑 Traits
  • Explain the purpose of the trait (marker? dynamic dispatch?).
  • Doc for each method -- include when/why to implement it.
  • Document clearly default implemented methods and when to override. 📦 Public Constants
  • Document what they configure and when you'd want to use them.

📌 Best Practices

  • ✅ Use examples generously -- they double as test cases.
  • ✅ Prefer clarity over formality -- it's for humans, not machines.
  • ✅ Prefer doc comments to explain usage, and leave implementation details to code comments if needed.
  • ✅ Use cargo doc --open to check your output often.
  • ✅ Add #![deny(missing_docs)] and other relevant doc lints in top-level modules if you want to enforce full doc coverage.

Source: SKILL.md on GitHub

No alerts3d5 checks · Risk SAFE
  • Gen Agent Trust Hub3d

    This skill provides a comprehensive guide to idiomatic Rust development based on Apollo GraphQL's best practices. It covers coding styles, error handling, performance optimization, and testing procedures using standard ecosystem tools.

  • Socket3d

    No alerts

  • Snyk3d

    Risk: LOW · No issues

  • Runlayer6mo

    1/10 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at 13ff457. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub yesterday.

Activeupdated 3 days ago
What it can do
Runs commands
compatibility
Rust 1.70+, Cargo
metadata
{
  "author": "apollographql",
  "version": "1.1.2"
}
All 1 allowed tools
Bash(cargo:*) Bash(rustc:*) Bash(rustfmt:*) Bash(clippy:*) Read Write Edit Glob Grep

README badge

README badge for apollographql/skills/rust-best-practices

Guides idiomatic Rust code review and writing using Apollo GraphQL's best practices handbook, covering ownership patterns, error handling, performance profiling, testing conventions, and type-state patterns. Use when writing new Rust, refactoring existing code, deciding between borrowing vs cloning, or implementing error handling and performance optimizations.

Generated from the current SKILL.md.

Does this skill cover async Rust patterns?
The skill references Apollo's best practices handbook which covers core ownership, borrowing, error handling, testing, and type patterns, but async-specific guidance is not listed in the chapter references provided.
Can I use this skill to lint my existing codebase?
Yes. The skill includes Clippy configuration and linting best practices, with specific commands like `cargo clippy --all-targets --all-features --locked -- -D warnings` to identify issues in your code.
Does this skill recommend anyhow or thiserror for error handling?
Yes. The skill specifies using `thiserror` for library errors and `anyhow` for binaries only, and advises never using `unwrap()` or `expect()` outside tests.
What Rust version does this require?
The skill requires Rust 1.70 or later and Cargo.
Does this cover the type state pattern?
Yes. Chapter 7 covers the type state pattern for encoding valid states in the type system to catch invalid operations at compile time, with a code example showing Connection states.

Generated from the current SKILL.md. These answers refresh after source changes.