Part 5 · 1 chapters · ~8 min

Idioms

Idiomatic Rust: error handling with thiserror and anyhow, builder patterns, newtypes and typestate, iterator chains, borrowing over cloning in hot paths, Cow, module and crate organisation, clippy's advice, and resisting writing Java or C++ in Rust.

9

Writing Rust like Rust

code
// library errors: precise types with thiserror; application errors: anyhow with context
#[derive(Debug, thiserror::Error)]
pub enum LedgerError {
    #[error("insufficient funds: available {available}, needed {needed}")] Insufficient { available: i64, needed: i64 },
    #[error("account {0} not found")] NotFound(String),
    #[error(transparent)] Db(#[from] sqlx::Error),
}
let cfg = load_config().context("loading ledger config")?;      // anyhow::Context adds a message chain

// accept borrowed, return owned; Cow when you might or might not need to allocate
fn normalise(name: &str) -> std::borrow::Cow<'_, str> {
    if name.chars().all(|c| c.is_ascii_uppercase()) { name.into() } else { name.to_ascii_uppercase().into() }
}
idiomaticinstead of
enums for states, matched exhaustivelystrings and booleans for state
? with typed errors.unwrap() in library code
iterators and combinatorsindex loops with manual bounds
composition and traitsattempting inheritance hierarchies
small crates in a cargo workspaceone giant crate (slow compiles)