# Rust Idioms and Design Patterns

## Contents

- [Idioms](#idioms)
  - [Borrowed types for arguments](#borrowed-types-for-arguments)
  - [String concatenation with format!](#string-concatenation-with-format)
  - [Constructors and Default](#constructors-and-default)
  - [Collections as smart pointers](#collections-as-smart-pointers)
  - [Finalisation in destructors](#finalisation-in-destructors)
  - [mem::take and mem::replace](#memtake-and-memreplace)
  - [On-stack dynamic dispatch](#on-stack-dynamic-dispatch)
  - [Iterating over Option](#iterating-over-option)
  - [Pass variables to closure](#pass-variables-to-closure)
  - [non_exhaustive for extensibility](#non_exhaustive-for-extensibility)
  - [Temporary mutability](#temporary-mutability)
  - [Return consumed argument on error](#return-consumed-argument-on-error)
- [Behavioural patterns](#behavioural-patterns)
  - [Command](#command)
  - [Interpreter](#interpreter)
  - [Newtype](#newtype)
  - [RAII with guards](#raii-with-guards)
  - [Strategy](#strategy)
  - [Visitor](#visitor)
- [Creational patterns](#creational-patterns)
  - [Builder](#builder)
  - [Fold](#fold)
- [Structural patterns](#structural-patterns)
  - [Struct decomposition for independent borrowing](#struct-decomposition-for-independent-borrowing)
  - [Prefer small crates](#prefer-small-crates)
  - [Contain unsafety in small modules](#contain-unsafety-in-small-modules)
  - [Custom traits for complex type bounds](#custom-traits-for-complex-type-bounds)
- [Functional patterns](#functional-patterns)

## Idioms

### Borrowed types for arguments

Prefer `&str` over `&String`, `&[T]` over `&Vec<T>`, and `&T` over `&Box<T>` in function parameters. This avoids unnecessary indirection and accepts more input types through deref coercion.

### String concatenation with format!

Prefer `format!("Hello {name}!")` over manual `push`/`push_str` chains for readability. For performance-critical paths where the string can be pre-allocated, manual push operations may be faster.

### Constructors and Default

Rust has no language-level constructors. Use an associated function called `new` to create objects. If the type has a sensible zero/empty state, implement the `Default` trait. It is common and expected to provide both `Default` and `new`, even if they are functionally identical.

`Default` enables usage with `or_default` functions throughout the standard library and partial initialization: `MyStruct { field: value, ..Default::default() }`.

### Collections as smart pointers

Implement `Deref` for owning collections to provide a borrowed view (e.g., `Vec<T>` → `&[T]`, `String` → `&str`). Implement methods on the borrowed view rather than the owning type where possible.

### Finalisation in destructors

Use `Drop` implementations to ensure cleanup code runs on all exit paths (early returns, `?`, panics). Assign the guard object to a named variable (not just `_`) to prevent immediate destruction. Destructors are not guaranteed to run in all cases (infinite loops, double panics).

### mem::take and mem::replace

When transforming an enum variant in place, use `mem::take(name)` to move values out without cloning. This avoids the "clone to satisfy the borrow checker" anti-pattern. For `Option` fields, prefer `Option::take()`.

### On-stack dynamic dispatch

When dynamic dispatch is needed but heap allocation is not, use `&mut dyn Trait` with temporary values. Since Rust 1.79.0, the compiler automatically extends lifetimes of temporaries in `&` or `&mut`.

### Iterating over Option

`Option` implements `IntoIterator`, so it works with `.extend()`, `.chain()`, and `for` loops. Use `std::iter::once` as a more readable alternative to `Some(foo).into_iter()` when the value is always present.

### Pass variables to closure

Use a separate scope block before the closure to prepare variables (clone, borrow, move) rather than creating separate named variables like `num2_cloned`. This groups captured state with the closure's definition.

### non_exhaustive for extensibility

Apply `#[non_exhaustive]` to public structs and enums that may gain fields or variants in the future, to maintain backwards compatibility across crate boundaries. Within a crate, a private field (e.g., `_b: ()`) achieves a similar effect. Use deliberately — incrementing the major version when adding fields or variants is often better.

### Temporary mutability

When data must be prepared mutably but then used immutably, use a nested block or variable rebinding (`let data = data;`) to enforce immutability after preparation.

### Return consumed argument on error

If a fallible function takes ownership of an argument, include that argument in the error type so the caller can recover it and retry. Example: `String::from_utf8` returns the original `Vec<u8>` inside `FromUtf8Error`.

## Behavioural patterns

### Command

Separate actions into objects and pass them as parameters. Three approaches: trait objects (complex commands with state), function pointers (simple stateless commands), and `Fn` trait objects (closures). Use trait objects when commands need multiple functions and state; use function pointers or closures for simple, stateless cases.

### Interpreter

Express recurring problem instances in a domain-specific language and implement an interpreter. `macro_rules!` can serve as a lightweight compile-time interpreter for simple DSLs.

### Newtype

Use a tuple struct with a single field to create a distinct type (e.g., `struct Password(String)`). Provides type safety, encapsulation, and the ability to implement custom traits on existing types. Zero-cost abstraction. Consider the `derive_more` crate to reduce boilerplate pass-through impls.

### RAII with guards

Tie resource acquisition to object creation and release to destruction (`Drop`). Use guard objects to mediate access — the borrow checker ensures references cannot outlive the guard. Classic example: `MutexGuard`.

### Strategy

Define an abstract algorithm skeleton and let implementations be swapped via traits or closures. Serde is an excellent real-world example: `Serialize`/`Deserialize` traits allow swapping formats transparently.

### Visitor

Encapsulate an algorithm operating over a heterogeneous collection without modifying the data types. Define `visit_*` methods on a `Visitor` trait for each type; provide `walk_*` helpers to factor out traversal. The visitor can be stateful.

## Creational patterns

### Builder

Construct complex objects step by step using a separate builder type. Provide a `builder()` method on the target type. Return the builder by value from each setter for method chaining: `FooBuilder::new().name("x").build()`. Consider the `derive_builder` crate.

### Fold

Transform a data structure by running an algorithm over each node, producing a new structure. Provide default `fold_*` methods that recurse into children, allowing implementors to override only the nodes they care about. Related to visitor, but produces a new structure.

## Structural patterns

### Struct decomposition for independent borrowing

When the borrow checker prevents simultaneous borrows of different fields in a large struct, decompose into smaller structs. Compose them back; each can then be borrowed independently. Often leads to better design.

### Prefer small crates

Build small, focused crates that do one thing well. Easier to understand, encourage modularity, and allow parallel compilation. Be mindful of dependency quality.

### Contain unsafety in small modules

Isolate `unsafe` code in the smallest possible module that upholds the needed invariants. Build a safe interface on top. This restricts the audit surface.

### Custom traits for complex type bounds

When trait bounds become unwieldy (especially with `Fn` traits), introduce a new trait with a generic `impl` for all types satisfying the original bound. Reduces verbosity and increases expressiveness.

## Functional patterns

Rust supports many functional paradigms alongside its imperative core:

- Prefer declarative iterator chains (`.fold()`, `.map()`, `.filter()`) over imperative loops when they improve clarity.
- Use generics as type classes. Rust's generic type parameters create type class constraints; different filled-in parameters create different types with potentially different `impl` blocks.
- Apply YAGNI — many traditional OO patterns are unnecessary in Rust due to traits, enums, and the type system.
