Rust programming expert including ownership, borrowing, lifetimes, async Tokio patterns, error handling, trait system, performance optimization, testing, and production systems development
Installer avec Codex ou Claude Copiez ce prompt, collez-le dans Codex, Claude ou un autre assistant, puis laissez-le vérifier la page du skill et l'installer pour vous.
Une commande directe contourne le prompt de vérification. Examinez la source avant de l'exécuter.
Rust programming expert including ownership, borrowing, lifetimes, async Tokio patterns, error handling, trait system, performance optimization, testing, and production systems development
version
2.0.0
model
sonnet
invoked_by
both
user_invocable
true
tools
["Read","Write","Edit","Bash","Grep","Glob"]
verified
true
lastVerifiedAt
"2026-02-19T00:00:00.000Z"
source
builtin
trust_score
100
provenance_sha
cd81656a1f27a001
Rust Expert
Apply idiomatic Rust patterns with strong safety, performance, and maintainability guarantees.
Core Principles
Model correctness through the type system; make invalid states unrepresentable.
Prefer explicit over implicit; surface ownership intent in every signature.
Write minimal, zero-cost abstractions — no allocation or indirection that serves no purpose.
Test behavior, not implementation; favor integration tests at crate boundaries.
1. Ownership, Borrowing, and Lifetimes
Ownership Patterns
// Prefer moves when value is consumed; prefer borrows when value is shared.fnprocess(data: Vec<u8>) ->Vec<u8> { /* consumes */ }
fninspect(data: &[u8]) ->usize { data.len() /* borrows */ }
// Clone only when necessary and explicitly documented.// Anti-pattern: clone() to silence borrow checker errors — fix the design instead.
Borrowing Rules (enforce at design time)
Only one mutable reference OR any number of immutable references at a time.
References must not outlive the value they point to.
Use Cow<'a, T> when you need borrow-or-own semantics without forcing allocation.
use std::borrow::Cow;
fnnormalize(s: &str) -> Cow<str> {
if s.contains(' ') {
Cow::Owned(s.replace(' ', "_"))
} {
Cow::(s)
}
}
else
Borrowed
Lifetime Patterns
// Explicit lifetime annotation: only when compiler cannot infer.structParser<'input> {
source: &'inputstr,
pos: usize,
}
impl<'input> Parser<'input> {
fnnext_token(&mutself) -> &'inputstr {
// Returns a slice of the original input — lifetime ties result to source.
&self.source[self.pos..]
}
}
// RPIT (Return Position Impl Trait) avoids lifetime noise in many cases.fnwords(s: &str) ->implIterator<Item = &str> {
s.split_whitespace()
}
Anti-Patterns to Avoid
clone() inside hot loops to avoid borrow checker.
unsafe to bypass lifetime checks — redesign instead.
'static bounds that force heap allocation when borrowing suffices.
Storing &mut T in structs — prefer owned data or RefCell<T>.
2. Error Handling
Decision Matrix
Scenario
Tool
Library crate errors
thiserror — typed, composable
Application / binary errors
anyhow — ergonomic ? chaining
Domain-specific context
Custom enum via thiserror
Infallible conversions
From / Into — zero overhead
thiserror (Library Crates)
use thiserror::Error;
#[derive(Debug, Error)]pubenumConfigError {
#[error("missing required field: {field}")]
MissingField { field: &'staticstr },
#[error("invalid value for {field}: {source}")]
ParseError { field: &'staticstr, #[source] source: std::num::ParseIntError },
#[error(transparent)]Io(#[from] std::io::Error),
}
anyhow (Application / Binary)
use anyhow::{Context, Result};
fnload_config(path: &str) ->Result<Config> {
letraw = std::fs::read_to_string(path)
.with_context(|| format!("reading config from {path}"))?;
toml::from_str(&raw).context("parsing config TOML")
}
Error Propagation Rules
Never use .unwrap() in library code; use .expect() only in tests and main() where the invariant is self-evident.
Add .context() / .with_context() at every error boundary to preserve the call chain.
Map errors at crate boundaries — do not leak internal error types in public APIs.
3. Trait System
Generics vs Trait Objects
// Prefer generics (monomorphized, zero-cost) when types are known at compile time.fnserialize<S: Serialize>(value: &S) ->Vec<u8> { /* ... */ }
// Use dyn Trait only when you need heterogeneous collections or runtime dispatch.fnhandlers() ->Vec<Box<dyn EventHandler>> { /* ... */ }
Where Clauses
// Prefer where clauses for readability when bounds are complex.fnmerge<K, V>(a: HashMap<K, V>, b: HashMap<K, V>) -> HashMap<K, V>
where
K: Eq + Hash,
V: Clone,
{ /* ... */ }
Blanket Implementations and Orphan Rules
Implement standard traits (Display, From, Iterator) for your types.
Respect the orphan rule: you may only implement a foreign trait for a local type.
Use the newtype pattern to work around orphan restrictions.
use tokio::task;
// Spawn a non-blocking async task.lethandle = task::spawn(asyncmove {
fetch_data(url).await
});
letresult = handle.await?; // propagate JoinError// CPU-bound work must go to the blocking thread pool — never block the async runtime.letresult = task::spawn_blocking(|| {
expensive_cpu_computation()
}).await?;
Prefer iterators over manual index loops — they compile to identical machine code.
Use #[inline] for hot, small functions that cross crate boundaries.
Prefer stack allocation; move to heap (Box, Vec, Arc) only when necessary.
SIMD
// Use std::simd (nightly) or portable-simd crate for explicit vectorization.// Profile first — LLVM auto-vectorizes most iterator chains.use std::simd::{f32x8, SimdFloat};
fndot_product_simd(a: &[f32], b: &[f32]) ->f32 {
a.chunks_exact(8)
.zip(b.chunks_exact(8))
.map(|(a_chunk, b_chunk)| {
letva = f32x8::from_slice(a_chunk);
letvb = f32x8::from_slice(b_chunk);
(va * vb).reduce_sum()
})
.sum()
}
Profiling
# flamegraph (install: cargo install flamegraph)
cargo flamegraph --bin my-app
# perf stat for CPU counters (Linux)
perf stat cargo run --release
# heaptrack for heap allocation analysis (Linux)
heaptrack cargo run --release
# criterion for micro-benchmarks
cargo bench
Allocation Awareness
// Preallocate when size is known.letmut v = Vec::with_capacity(expected_len);
// String building: use write! into a pre-allocated String.use std::fmt::Write;
letmut s = String::with_capacity(256);
write!(s, "id={}", id)?;
// Avoid format!() in hot paths — prefer direct write!() or push_str().
Use descriptive names: <function>_<scenario>_<expected>.
Mock only external I/O boundaries (HTTP, filesystem, database).
Run cargo test -- --nocapture for diagnostic output during development.
Run cargo nextest run for faster parallel test execution in CI.
8. Unsafe Rust
When to Use
FFI boundaries (calling C functions, exporting to C).
Low-level memory mapping or SIMD intrinsics when safe abstractions cannot reach.
Performance-critical code where the safe alternative has measurable overhead (proved by profiling).
Guidelines
/// # Safety////// - `ptr` must be non-null and properly aligned for `T`./// - The memory at `ptr` must be valid for `len` elements of type `T`./// - The caller must ensure no other mutable references to the memory exist.pubunsafefnfrom_raw_parts<T>(ptr: *const T, len: usize) -> &'static [T] {
std::slice::from_raw_parts(ptr, len)
}
Every unsafe block must have a // SAFETY: comment explaining why it is sound.
Minimize the scope of unsafe — wrap in a safe abstraction immediately.
Prefer unsafe fn over unsafe block inside a safe fn when the entire function is unsafe.
Use Miri (cargo +nightly miri test) to detect undefined behavior in unsafe code.
9. FFI and Interop
Exporting to C
#[no_mangle]pubextern"C"fnmy_add(a: i32, b: i32) ->i32 {
a + b
}
Calling C from Rust
extern"C" {
fnstrlen(s: *const std::os::raw::c_char) ->usize;
}
fnrust_strlen(s: &str) ->usize {
letcstr = std::ffi::CString::new(s).expect("no null bytes");
// SAFETY: cstr is a valid, null-terminated C string.unsafe { strlen(cstr.as_ptr()) }
}
cbindgen / bindgen
Use cbindgen to generate C headers from Rust public API.
Use bindgen to generate Rust bindings from C headers.
Always run through CI to detect API drift.
10. Build System
Cargo Workspaces
# Cargo.toml (workspace root)[workspace]members = ["crates/core", "crates/server", "crates/cli"]
resolver = "2"[workspace.dependencies]tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
# crates/core/Cargo.toml[dependencies]tokio.workspace = trueserde.workspace = true
// build.rs — runs before crate compilation.fnmain() {
// Rerun when proto files change.println!("cargo:rerun-if-changed=proto/");
// Link a system library.println!("cargo:rustc-link-lib=ssl");
}
Useful Cargo Commands
cargo build --release # optimized build
cargo clippy -- -D warnings # lint (treat warnings as errors)
cargo fmt --check # format check (CI)
cargo doc --no-deps --open # generate and open docs
cargo audit # check dependencies for CVEs
cargo deny check # license + advisory checks
cargo expand# show macro expansion