Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

House Style and Rust Philosophy

Rule

Write idiomatic Rust with an OO-leaning default: model domain concepts as structs with methods, protect invariants, and compose behavior explicitly.

Why

Rust supports data with behavior without inheritance. Clear ownership and composition provide structure without forcing object-oriented patterns that do not fit the language.

Do

  • Start with domain types when values carry distinct meaning.
  • Put behavior on the type that owns the relevant data or invariant.
  • Prefer direct composition with explicit fields and methods.
  • Use free functions for algorithms or cross-type operations with no natural receiver.
  • Use functional pipelines when they are clearer than stateful updates.
  • Follow the owner pages for fields, traits, and iteration.

Avoid

  • Do not emulate inheritance hierarchies with traits, enums, or nested wrappers.
  • Do not move natural type behavior into stateless helper modules.
  • Do not expose helpers publicly only to make private behavior testable.
  • Do not create pass-through wrappers or delegation machinery around a confused boundary.
  • Do not choose a design-pattern name over simpler Rust types, modules, and ownership.

Example

#![allow(unused)]
fn main() {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Money {
    cents: u64,
}

impl Money {
    pub const ZERO: Self = Self { cents: 0 };

    pub fn checked_add(self, other: Self) -> Option<Self> {
        self.cents
            .checked_add(other.cents)
            .map(|cents| Self { cents })
    }
}
}

Exceptions

  • Use plain data structs when their fields are the complete API and carry no invariants.
  • Introduce a trait before a second implementation only when callers already need substitution or a testing seam.