Here are the most important principles to keep in mind when writing Rust code:
- Every value has one owner — when the owner goes out of scope, the value is dropped.
- Borrow rules: you can have either one mutable reference or any number of immutable references — never both at the same time.
- Prefer borrowing (
&T) over cloning unless you truly need ownership.
- Use the type system to make illegal states unrepresentable — encode invariants in types, not runtime checks.
- Prefer
enumwith variants over boolean flags or stringly-typed logic. - Newtype pattern (
struct Meters(f64)) to prevent mixing incompatible values.
- Use
Result<T, E>for recoverable errors — never ignore them. - Use
?to propagate errors cleanly. - Avoid
unwrap()andexpect()in production code; reserve them for prototypes or truly infallible cases. - Define meaningful error types (or use
thiserror/anyhowcrates).
- Rust has no hidden control flow — be explicit about lifetimes, mutability (
mut), and conversions. - Avoid
unsafeunless absolutely necessary, and isolate it when you must use it. - Don't fight the borrow checker — if something feels hard, reconsider the design.
- Use iterators and closures freely — they compile down to the same machine code as hand-written loops.
- Avoid unnecessary heap allocations (
Box,Vec,String) when stack types or slices suffice. - Understand
&strvsStringand&[T]vsVec<T>.
- Rust's type system prevents data races at compile time — trust it.
- Prefer message passing (
std::sync::mpsc, channels) over shared state where possible. - When sharing state, use
Arc<Mutex<T>>correctly and keep locks short-lived.
- Follow
rustfmtformatting andclippylint suggestions — they encode community best practices. - Implement standard traits (
Display,Debug,From,Into,Iterator) where appropriate. - Use
impl Traitin function signatures to keep APIs flexible.
- Validate at construction time (constructors that return
Result), not at use time. - Use the typestate pattern to enforce state machine transitions through the type system.
- Think in terms of what the compiler can guarantee, not just what the runtime will do.
The overarching philosophy is: if it compiles, it probably works correctly — Rust's whole design pushes bugs from runtime into compile time. Trust and work with the type system rather than against it.