Rust Development Guide

This guide covers comprehensive Rust development guidance including code organization, patterns, architectural decisions, and formatting standards. For general guidance applicable to all languages, see the main practices guide. For cross-language formatting and shared workflow guidance, see the code style guide, environment guide, validation guide, testing guide, and release guide.

Module Organization

  • Follow standard Rust module organization patterns:

    • src/lib.rs or src/main.rs as the crate root

    • Use src/module.rs files with src/module/ directories for submodules

    • Organize related functionality into logical modules

    • Re-export important items at appropriate levels using pub use

  • Apply project nomenclature guidelines from the nomenclature guide when naming modules, following Rust’s snake_case convention for module names.

Naming Conventions

  • Follow standard Rust naming conventions as defined in the Rust API Guidelines:

    • Types, traits, enums: PascalCase

    • Functions, variables, modules: snake_case

    • Constants, statics: SCREAMING_SNAKE_CASE

    • Macros: snake_case (by convention, though not enforced)

  • Apply project nomenclature guidelines from the nomenclature guide when naming types, functions, and variables.

Documentation

Content Standards

  • Write documentation comments in narrative mood (third person) consistent with project documentation standards.

  • Use standard Rust documentation patterns:

    • /// for public API documentation

    • //! for module-level documentation

    • Include examples in documentation when helpful

  • Document error conditions using the standard # Errors section:

    /// Validates user configuration.
    ///
    /// # Errors
    ///
    /// Returns `CrateError::Validation` if the configuration is invalid.
    pub fn validate_config(config: &Config) -> Result<(), CrateError> {
        // implementation
    }
    

Visual Formatting

  • Use standard Rust documentation comments (/// for items, //! for modules).

  • Write documentation in narrative mood (third person) consistent with project documentation conventions.

    ✅ Prefer:

    /// Validates the configuration data.
    /// 
    /// Returns the validated configuration or an error if validation fails.
    pub fn validate_configuration(config: &Config) -> Result<Config, ConfigError> {
        // implementation
    }
    

    ❌ Avoid:

    /// Validate the configuration data.
    pub fn validate_configuration(config: &Config) -> Result<Config, ConfigError> {
        // implementation  
    }
    

Formatting Standards

  • Follow the standard Rust formatting conventions enforced by rustfmt. The project’s .rustfmt.toml configuration defines the specific formatting rules.

  • Use cargo fmt to automatically format code according to project standards.

  • Maximum line length follows the general project standard of 79 columns, which may be configured in .rustfmt.toml if different from Rust defaults.

Testing

Language-neutral public-contract rules live in the testing guide. This section states how those rules apply in Rust crates.

Test Layout

  • Prefer tests under tests/unit and tests/integration over inline #[cfg(test)] modules in src/**.

  • Prefer tests that exercise public interfaces. Avoid source-inclusion patterns used only to reach private internals.

  • External unit tests are still unit tests. Requiring public contracts does not mean every test must be a multi-crate integration or end-to-end check.

Inline #[cfg(test)] Modules

Inline #[cfg(test)] modules used to access crate-private items are a last resort for behavior that cannot reasonably be tested through public contracts or a clean injection seam. They are not a convenience escape hatch.

Permit an inline #[cfg(test)] block only when all of the following hold:

  1. The tested item is crate-private by design (not by oversight or laziness), and making it testable externally would require widening its visibility or adding a #[doc(hidden)] pub escape hatch that would itself become unintended API surface.

  2. No existing public interface exercises the same code path.

  3. The inline test block contains at most one #[test] function.

If a candidate inline test fails any of these conditions, move it to tests/unit and introduce a narrow seam, restructure ownership, or otherwise keep the public surface intentional. Do not default to inline access to avoid that conversation; the friction is intentional.

Private Algorithms

Genuinely private algorithms may still deserve focused coverage. Prefer, in order:

  1. Exercise the algorithm through the public capability that owns it.

  2. Inject a narrow collaborator or strategy so the private path becomes reachable without exporting internals.

  3. Only then use a tightly scoped inline #[cfg(test)] test that meets the criteria above.

Do not widen pub visibility or add test-only re-exports merely to satisfy coverage tooling.

Future Development