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() }
}| idiomatic | instead of |
|---|---|
| enums for states, matched exhaustively | strings and booleans for state |
? with typed errors | .unwrap() in library code |
| iterators and combinators | index loops with manual bounds |
| composition and traits | attempting inheritance hierarchies |
| small crates in a cargo workspace | one giant crate (slow compiles) |