Rust Core Toolchain Conventions
Edition and Toolchain
- Target Rust 2024 edition. MSRV is set by the most-constrained transitive dependency,
not the edition floor — pin it explicitly in
Cargo.toml (rust-version = "...").
- Gate every change on
cargo fmt --check, cargo clippy -- -D warnings, and cargo test.
- Prefer
cargo nextest run for faster, isolated test execution when available.
Ownership and Borrowing
- Borrow by default, own only when necessary. Accept
&str / &[T] in function
signatures, not String / Vec<T>, unless the function must take ownership.
- Return owned values; let callers borrow. Avoid returning references tied to locals.
- Reach for
Cow<'_, str> when a value is usually borrowed but occasionally owned.
- Use
Rc/Arc only for genuine shared ownership; prefer passing references first.
- Interior mutability:
Cell/RefCell for single-threaded, Mutex/RwLock (under Arc)
for shared-state concurrency. Never hold a lock across an .await.
Error Handling
- Never
unwrap()/expect()/panic! in library code. Return Result<T, E>.
- Libraries: define a typed error enum with
thiserror. Applications: anyhow::Result
with .context(...) to add provenance at each boundary.
- Use the
? operator for propagation; convert errors with From/#[from].
- Reserve
panic! for truly unreachable invariants, and document why.
Async with tokio
- Use
#[tokio::main] (or an explicit runtime) and async/.await for I/O-bound work.
- Spawn concurrent work with
tokio::spawn; join with tokio::join! or try_join!.
- Apply explicit timeouts via
tokio::time::timeout on all external calls.
- Offload CPU-bound work with
tokio::task::spawn_blocking — never block the runtime.
- Hold no
std::sync lock across an .await; use tokio::sync primitives instead.
Project Structure and Idioms
- One responsibility per module; expose a curated public API via
pub in lib.rs.
- Derive
Debug, and Clone/PartialEq/Eq where cheap and meaningful.
- Model state with enums + exhaustive
match; avoid boolean-flag soup.
- Prefer iterator chains over manual index loops; they are clearer and often faster.
- Use newtypes (
struct UserId(u64)) to make illegal states unrepresentable.
- Write doctests for public functions; they double as runnable documentation.