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

Control Flow

Rule

Use explicit clarity-first branches and keep mutation in small scopes after fallible validation.

Why

Visible exits, exhaustive state handling, and atomic-looking updates make invariants easier to preserve during later changes.

Do

  • Use ? when the current function only propagates failure.
  • Use early returns for invalid inputs, missing prerequisites, and permission failures.
  • Use let else when a missing pattern exits the current scope.
  • Use if let for one special pattern and while let for repeated extraction.
  • Use match when several variants or exhaustiveness matter.
  • Keep arms and guards small and name meaningful variants instead of _.
  • Keep the happy path linear after validation.
  • Use small mutable locals for accumulators, builders, and staged values.
  • Compute and validate before mutating long-lived state when partial updates would be invalid.

Avoid

  • Do not match on bool.
  • Do not hide meaningful states behind _.
  • Do not deeply nest branches when guard clauses clarify exits.
  • Do not use let else for substantial recovery logic.
  • Do not replace error handling with unwrap or expect.
  • Do not mutate persistent state before fallible validation unless partial progress is intentional.
  • Follow Option and Result for combinator choices.

Example

#![allow(unused)]
fn main() {
pub fn plan_action(request: Request) -> Result<Action, Error> {
    let Some(user_id) = request.user_id() else {
        return Err(Error::MissingUserId);
    };

    let command = Command::parse(request.command())?;
    if !request.permissions().can_run(user_id, &command) {
        return Err(Error::Forbidden { user_id });
    }

    match command {
        Command::Start(target) => Ok(Action::Start(Target::try_new(target)?)),
        Command::Stop(target) => Ok(Action::Stop(target)),
        Command::Status => Ok(Action::Status),
    }
}
}

Exceptions

  • Use _ for intentionally ignored test or metrics cases and external non-exhaustive enums.
  • Mutate incrementally when every step is independently valid and no rollback exists.