بنقرة واحدة
toolchains-rust-core
// Rust 2024 edition core patterns: idiomatic code, error handling, traits/generics, macros, async/concurrency, testing, and project architecture
// Rust 2024 edition core patterns: idiomatic code, error handling, traits/generics, macros, async/concurrency, testing, and project architecture
Complete circuit breaker enforcement patterns with examples and remediation
Catalog and router for migration skill wizards (service installation guides)
Install and configure trusty-memory and trusty-search for persistent AI memory and semantic code search
General protocol for executing migration skill wizards - service installation and configuration guides
Persistent memory palace system with hierarchical storage (palace/wing/room/closet/drawer), progressive retrieval (L0-L3), and temporal knowledge graph for cross-session context
Hybrid code search (BM25 + vector + KG) with RRF fusion. Single daemon serves multiple named indexes. Replaces mcp-vector-search.
| name | toolchains-rust-core |
| description | Rust 2024 edition core patterns: idiomatic code, error handling, traits/generics, macros, async/concurrency, testing, and project architecture |
| version | 2.0.0 |
| category | toolchains-rust |
| tags | ["rust","patterns","performance","minimalism","efficiency","safety","async","testing","architecture"] |
| effort | medium |
# Cargo.toml - essential stack
[dependencies]
tokio = { version = "1", features = ["full"] }
thiserror = "2"
anyhow = "2"
serde = { version = "1", features = ["derive"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
[dev-dependencies]
rstest = "0.22"
proptest = "1"
mockall = "0.13"
insta = "1"
criterion = { version = "0.5", features = ["html_reports"] }
cargo add tokio --features full && cargo add thiserror anyhow serde --features serde/derive
cargo clippy -- -D warnings # enforce in CI
cargo test # unit + integration + doctests
cargo test --doc # doctests only
self methods; finish with build() -> Result<T, E> for validated construction.struct UserId(u64) gives type safety at zero cost; add impl From<u64> for UserId.From<T> only — Into<T> is blanket-provided; accept impl Into<T> in function params.#[derive(Debug, Clone, PartialEq, Eq, Hash)] on domain types; add serde only when needed.&str not &String, &[T] not &Vec<T>, impl AsRef<Path> not &PathBuf.Config { field: value, ..Default::default() } — audit all fields when adding new ones.Drop for resource cleanup; wrap locks, file handles, and connections in guard types.Cow<'_, str>: Return Cow<'_, str> when sometimes borrowing, sometimes owning — avoids unnecessary clones.Box only for trait objects, recursive types, or large data escaping scope.use thiserror::Error;
#[derive(Error, Debug)]
pub enum UserError {
#[error("user {id} not found")]
NotFound { id: u64 },
#[error("invalid email: {0}")]
InvalidEmail(String),
#[error("database error")]
Database(#[from] sqlx::Error),
#[error("serialization failed")]
Serialization(#[from] serde_json::Error),
}
Rules:
String or Box<dyn Error> in public library API.#[from] generates From<SourceError> — use ? to propagate automatically.#[source] exposes the underlying error via Error::source() without auto-From.use anyhow::{Context, Result};
fn load_config(path: &Path) -> Result<Config> {
let text = std::fs::read_to_string(path)
.with_context(|| format!("reading config from {}", path.display()))?;
let config: Config = toml::from_str(&text)
.context("parsing config as TOML")?;
Ok(config)
}
fn main() -> Result<()> {
let config = load_config(Path::new("config.toml"))?;
run(config)
}
Rules:
anyhow::Result<T> in main(), CLI handlers, and top-level application code..context("what we were doing") at each call site.anyhow in public library APIs — wrap and convert at the boundary.// In application code: convert library error to anyhow
let user = user_service.get(id)
.await
.map_err(|e| anyhow::anyhow!("get user {id}: {e}"))?;
// Or implement From in your app error type
impl From<UserError> for AppError { ... }
// Prefer small traits over large catch-all interfaces
trait Fetch {
async fn fetch(&self, url: &str) -> Result<Bytes, FetchError>;
}
trait Store {
async fn store(&self, key: &str, value: &[u8]) -> Result<(), StoreError>;
}
// Compose with supertraits
trait Cache: Fetch + Store + Send + Sync {}
// Generics: compile-time dispatch, monomorphized, no heap alloc
fn process<F: Fetch>(fetcher: &F) -> Result<()> { ... }
// Trait objects: runtime dispatch, heap alloc, heterogeneous collections
fn process_dyn(fetcher: &dyn Fetch) -> Result<()> { ... }
fn process_arc(fetcher: Arc<dyn Fetch>) -> Result<()> { ... }
// Rule: default to generics; use dyn Trait for:
// - Heterogeneous collections (Vec<Box<dyn Trait>>)
// - Runtime polymorphism (DI containers, plugin systems)
// - Avoiding monomorphization bloat in large codebases
// Associated type: one implementation per type (Iterator pattern)
trait Parser {
type Output;
fn parse(&self, input: &str) -> Result<Self::Output, ParseError>;
}
// Generic parameter: multiple implementations per type
trait Convert<T> {
fn convert(&self) -> T;
}
// allows: impl Convert<String> for MyType AND impl Convert<u64> for MyType
// Inline bounds for simple cases
fn clone_and_display<T: Clone + Display>(value: &T) { ... }
// Where clause for complex cases (preferred for readability)
fn process<T, E>(items: &[T]) -> Result<Vec<String>, E>
where
T: Display + Send + 'static,
E: std::error::Error + From<fmt::Error>,
{ ... }
// Provide default impl for all qualifying types
trait Summarize {
fn summary(&self) -> String;
}
impl<T: Display> Summarize for T {
fn summary(&self) -> String {
format!("{}", self)
}
}
trait IteratorExt: Iterator {
fn take_while_inclusive<P>(self, predicate: P) -> TakeWhileInclusive<Self, P>
where
P: FnMut(&Self::Item) -> bool,
Self: Sized;
}
impl<I: Iterator> IteratorExt for I {
fn take_while_inclusive<P>(self, predicate: P) -> TakeWhileInclusive<Self, P>
where P: FnMut(&Self::Item) -> bool, Self: Sized {
TakeWhileInclusive { iter: self, predicate, done: false }
}
}
mod private {
pub trait Sealed {}
}
pub trait MyTrait: private::Sealed {
fn method(&self);
}
// Only types you impl private::Sealed for can impl MyTrait
impl private::Sealed for MyType {}
impl MyTrait for MyType { fn method(&self) {} }
// Stable since Rust 1.65 — enables lifetime-parameterized associated types
trait Lender {
type Item<'a> where Self: 'a;
fn lend(&mut self) -> Self::Item<'_>;
}
macro_rules!)// Pattern matching on token trees
macro_rules! hashmap {
($($key:expr => $val:expr),* $(,)?) => {{
let mut map = std::collections::HashMap::new();
$(map.insert($key, $val);)*
map
}};
}
let m = hashmap!{ "a" => 1, "b" => 2 };
// Repeat patterns
macro_rules! impl_from_str {
($($t:ty),+) => {
$(impl std::str::FromStr for $t {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.parse::<u64>().map(Self).map_err(Into::into)
}
})+
};
}
// Custom derive — separate crate required (e.g., my-macros-derive)
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};
#[proc_macro_derive(Builder)]
pub fn derive_builder(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let name = &ast.ident;
let expanded = quote! {
impl #name {
pub fn builder() -> #nameBuilder { Default::default() }
}
};
expanded.into()
}
Rules:
$crate:: prefix for items referenced inside the macro.derive_more and strum crates over writing your own derive macros.trybuild to verify compile-time error messages.#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Tokio only — async-std is effectively unmaintained as of 2026
run().await
}
// Async closures (Rust 2024)
let handler = async |req: Request| -> Result<Response> {
process(req).await
};
use tokio::{join, select};
// Concurrent execution (all complete)
let (users, orders) = join!(fetch_users(), fetch_orders());
// Racing (first to complete wins)
select! {
result = fetch_with_primary() => result,
result = fetch_with_fallback() => result,
_ = tokio::time::sleep(Duration::from_secs(5)) => Err(TimeoutError),
}
// Dynamic task groups
use tokio::task::JoinSet;
let mut set = JoinSet::new();
for id in ids {
set.spawn(async move { fetch_item(id).await });
}
while let Some(result) = set.join_next().await {
handle(result??);
}
// Arc<Mutex<T>> for mutable shared state
let state: Arc<Mutex<HashMap<u64, User>>> = Arc::new(Mutex::new(HashMap::new()));
let state_clone = Arc::clone(&state);
tokio::spawn(async move {
let mut guard = state_clone.lock().await; // tokio::sync::Mutex
guard.insert(1, user);
});
// RwLock for read-heavy workloads
let cache: Arc<RwLock<HashMap<String, Value>>> = Arc::new(RwLock::new(HashMap::new()));
let val = cache.read().await.get("key").cloned();
// Atomics for simple flags/counters — no mutex overhead
use std::sync::atomic::{AtomicU64, Ordering};
let counter = Arc::new(AtomicU64::new(0));
counter.fetch_add(1, Ordering::SeqCst);
use tokio::sync::mpsc;
enum Command {
Get { key: String, reply: tokio::sync::oneshot::Sender<Option<String>> },
Set { key: String, value: String },
Shutdown,
}
struct CacheActor {
store: HashMap<String, String>,
rx: mpsc::Receiver<Command>,
}
impl CacheActor {
async fn run(mut self) {
while let Some(cmd) = self.rx.recv().await {
match cmd {
Command::Get { key, reply } => {
let _ = reply.send(self.store.get(&key).cloned());
}
Command::Set { key, value } => {
self.store.insert(key, value);
}
Command::Shutdown => break,
}
}
}
}
// Handle is cheaply cloneable — clone per task/thread
#[derive(Clone)]
struct CacheHandle(mpsc::Sender<Command>);
impl CacheHandle {
async fn get(&self, key: String) -> Option<String> {
let (tx, rx) = tokio::sync::oneshot::channel();
self.0.send(Command::Get { key, reply: tx }).await.ok()?;
rx.await.ok()?
}
async fn set(&self, key: String, value: String) {
let _ = self.0.send(Command::Set { key, value }).await;
}
}
// RAII cleanup: use Drop for cancellation handling
struct Guard {
resource: Resource,
}
impl Drop for Guard {
fn drop(&mut self) {
// Always runs, even if task is cancelled
self.resource.cleanup();
}
}
// Avoid holding mutable state across .await points
// BAD:
let mut locked = mutex.lock().await;
do_io().await; // lock held across await — deadlock risk
drop(locked);
// GOOD:
{
let mut locked = mutex.lock().await;
update_state(&mut locked);
} // lock released before await
do_io().await;
// Never block the async executor
let result = tokio::task::spawn_blocking(|| {
// CPU-intensive or blocking sync code here
heavy_computation()
})
.await?;
// Rayon for data-parallel CPU work
use rayon::prelude::*;
let result: Vec<_> = data.par_iter().map(|x| transform(x)).collect();
// Unit tests: same file, cfg(test) module
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_basic_operation() {
// Arrange
let sut = MyStruct::new(42);
// Act
let result = sut.compute();
// Assert
assert_eq!(result, 84);
}
}
// Integration tests: tests/ directory, no cfg(test) needed
// tests/integration_test.rs
use my_crate::Api;
#[tokio::test]
async fn full_roundtrip() {
let api = Api::start_test_server().await;
// ...
}
use rstest::*;
#[fixture]
fn database() -> TestDb {
TestDb::in_memory()
}
#[rstest]
#[case("valid@email.com", true)]
#[case("not-an-email", false)]
#[case("", false)]
fn test_email_validation(#[case] input: &str, #[case] expected: bool) {
assert_eq!(is_valid_email(input), expected);
}
#[rstest]
async fn test_with_fixture(database: TestDb) {
let repo = UserRepository::new(database);
assert!(repo.find(999).await.unwrap().is_none());
}
use proptest::prelude::*;
proptest! {
#[test]
fn roundtrip_serialization(s in "\\PC*") {
let encoded = encode(&s);
let decoded = decode(&encoded).unwrap();
prop_assert_eq!(s, decoded);
}
#[test]
fn sort_is_idempotent(mut v in prop::collection::vec(any::<i32>(), 0..100)) {
v.sort();
let sorted_once = v.clone();
v.sort();
prop_assert_eq!(sorted_once, v);
}
}
use insta::assert_snapshot;
#[test]
fn test_error_message() {
let err = UserError::NotFound { id: 42 };
assert_snapshot!(err.to_string());
// First run writes snapshot; subsequent runs compare
// Review with: cargo insta review
}
#[test]
fn test_json_output() {
let user = User { id: 1, name: "Alice".into() };
insta::assert_json_snapshot!(user, @r###"
{
"id": 1,
"name": "Alice"
}
"###);
}
use mockall::{automock, predicate::*};
#[cfg_attr(test, automock)]
trait EmailService: Send + Sync {
async fn send(&self, to: &str, body: &str) -> Result<(), EmailError>;
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_sends_welcome_email() {
let mut mock = MockEmailService::new();
mock.expect_send()
.with(eq("user@example.com"), contains("Welcome"))
.times(1)
.returning(|_, _| Ok(()));
let service = UserService::new(Arc::new(mock));
service.register("user@example.com").await.unwrap();
}
}
// Single-threaded (default)
#[tokio::test]
async fn test_basic_async() { ... }
// Multi-threaded runtime for testing concurrent behavior
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn test_concurrent_writes() { ... }
/// Parses a user ID from a string.
///
/// # Examples
///
/// ```
/// use my_crate::UserId;
///
/// let id = UserId::parse("42").unwrap();
/// assert_eq!(id.value(), 42);
/// ```
///
/// ```should_panic
/// use my_crate::UserId;
/// UserId::parse("not-a-number").unwrap(); // panics
/// ```
pub fn parse(s: &str) -> Result<UserId, ParseError> { ... }
// Cargo.toml in fuzz/ directory
// [dependencies]
// libfuzzer-sys = "0.4"
// fuzz/fuzz_targets/parse_input.rs
#![no_main]
use libfuzzer_sys::fuzz_target;
fuzz_target!(|data: &[u8]| {
if let Ok(s) = std::str::from_utf8(data) {
let _ = my_crate::parse(s); // Must not panic
}
});
// Run: cargo fuzz run parse_input
my-project/
├── Cargo.toml # [workspace] members = [...]
├── crates/
│ ├── domain/ # Core types, traits, no I/O deps
│ │ └── src/lib.rs
│ ├── infrastructure/ # DB, HTTP clients, external APIs
│ │ └── src/lib.rs
│ ├── application/ # Business logic, orchestration
│ │ └── src/lib.rs
│ └── api/ # HTTP layer (axum/actix-web)
│ └── src/
│ ├── lib.rs
│ └── main.rs
└── tests/
└── integration/
Rules:
domain must not depend on infrastructure — only pure types and traits.application depends on domain traits, not infrastructure concretions.infrastructure implements domain traits; injected at startup in main.[features]
default = ["tokio-runtime"]
tokio-runtime = ["dep:tokio"]
async-std-runtime = ["dep:async-std"]
serde = ["dep:serde", "dep:serde_json"]
tracing = ["dep:tracing"]
# Optional dependencies (activated by feature)
[dependencies]
tokio = { version = "1", optional = true }
serde = { version = "1", optional = true }
// Guard feature-specific code
#[cfg(feature = "serde")]
impl Serialize for MyType { ... }
#[cfg(feature = "tracing")]
tracing::info!("processing item");
// lib.rs: re-export public API cleanly
pub use self::user::{User, UserId, UserError};
pub use self::service::UserService;
mod user; // user.rs or user/mod.rs
mod service; // service.rs
// Keep one public type per file for complex types
// Internal modules use pub(crate) / pub(super) liberally
// AppState: clone-on-use (cheap because Arc internals)
#[derive(Clone)]
struct AppState {
users: Arc<dyn UserRepository>,
cache: Arc<dyn Cache>,
config: Arc<Config>, // immutable after startup
}
// Pass via axum/actix State extractor, not global statics
// Never use lazy_static or once_cell for mutable service state
use tokio::signal;
async fn run() -> anyhow::Result<()> {
let (shutdown_tx, shutdown_rx) = tokio::sync::broadcast::channel(1);
let server = tokio::spawn(serve(shutdown_rx));
// Wait for Ctrl+C or SIGTERM
signal::ctrl_c().await?;
let _ = shutdown_tx.send(());
server.await??;
Ok(())
}
async fn serve(mut shutdown: tokio::sync::broadcast::Receiver<()>) {
loop {
tokio::select! {
_ = accept_connection() => { /* handle */ }
_ = shutdown.recv() => break,
}
}
}
Cow before reaching for .clone().SmallVec<[T; N]>: Use for vectors usually small; improves cache locality.bumpalo for batch-allocating many short-lived objects.String::with_capacity(n), Vec::with_capacity(n) when final size is predictable.cargo bench gives statistical benchmarking with warmup.[profile.release] debug = true then cargo flamegraph.lto = "thin" + codegen-units = 1 in release profile.unsafe blocks as small as possible; wrap in safe abstractions.unsafe block requires // SAFETY: explaining invariants.unsafe fn: Function bodies are no longer implicitly unsafe.unsafe extern blocks: Rust 2024 requires unsafe extern "C" { ... }.Send + Sync: Auto-implemented when all fields qualify; verify with static_assertions.Cell<T> for Copy, RefCell<T> single-threaded, Mutex<T> multi-threaded.Cow.std::fs::read, std::net, or thread::sleep in async; use tokio:: equivalents.unwrap() in production: Replace with ?, .unwrap_or_default(), .expect("invariant: reason").String or Box<dyn Error> in library APIs — define typed enums with thiserror.Deref for inheritance: Do not implement Deref to simulate OOP — use trait composition.env::set_var without unsafe: In Rust 2024, set_var/remove_var are unsafe.cargo clippy -- -D warnings in CI; use #[allow] with justification, never blanket silencing.lazy_static/once_cell for mutable services — constructor inject.